aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile15
-rw-r--r--tools/Rules.mk (renamed from tools/Make.defs)13
-rw-r--r--tools/blktap/Makefile100
-rw-r--r--tools/blktap/README149
-rw-r--r--tools/blktap/blkaio.c19
-rw-r--r--tools/blktap/blkaiolib.c489
-rw-r--r--tools/blktap/blkaiolib.h16
-rw-r--r--tools/blktap/blkcow.c31
-rw-r--r--tools/blktap/blkcowgnbd.c24
-rw-r--r--tools/blktap/blkcowimg.c24
-rw-r--r--tools/blktap/blkcowlib.c380
-rw-r--r--tools/blktap/blkcowlib.h14
-rw-r--r--tools/blktap/blkdump.c151
-rw-r--r--tools/blktap/blkgnbd.c19
-rw-r--r--tools/blktap/blkgnbdlib.c471
-rw-r--r--tools/blktap/blkgnbdlib.h16
-rw-r--r--tools/blktap/blkimg.c19
-rw-r--r--tools/blktap/blkimglib.c325
-rw-r--r--tools/blktap/blkimglib.h16
-rw-r--r--tools/blktap/blkint.h105
-rw-r--r--tools/blktap/blktaplib.c542
-rw-r--r--tools/blktap/blktaplib.h76
-rw-r--r--tools/blktap/libgnbd/Makefile8
-rw-r--r--tools/blktap/libgnbd/gnbdtest.c90
-rw-r--r--tools/blktap/libgnbd/libgnbd.c647
-rw-r--r--tools/blktap/libgnbd/libgnbd.h25
-rw-r--r--tools/check/Makefile16
-rw-r--r--tools/check/README20
-rwxr-xr-xtools/check/check_brctl9
-rwxr-xr-xtools/check/check_curl_devel11
-rwxr-xr-xtools/check/check_curl_lib10
-rwxr-xr-xtools/check/check_logging30
-rwxr-xr-xtools/check/check_python10
-rwxr-xr-xtools/check/check_twisted46
-rwxr-xr-xtools/check/check_zlib_devel10
-rwxr-xr-xtools/check/check_zlib_lib10
-rwxr-xr-xtools/check/chk72
-rw-r--r--tools/examples/Makefile56
-rw-r--r--tools/examples/README135
-rwxr-xr-xtools/examples/block-enbd33
-rwxr-xr-xtools/examples/block-file31
-rw-r--r--tools/examples/bochsrc20
-rwxr-xr-xtools/examples/init.d/xend28
-rwxr-xr-xtools/examples/init.d/xendomains154
-rw-r--r--tools/examples/mem-map.sxp10
-rwxr-xr-xtools/examples/network75
-rw-r--r--tools/examples/network-nat77
-rwxr-xr-xtools/examples/network-route19
-rwxr-xr-xtools/examples/vif-bridge6
-rw-r--r--tools/examples/vif-nat66
-rwxr-xr-xtools/examples/vif-route76
-rw-r--r--tools/examples/vifctl149
-rw-r--r--tools/examples/xend-config.sxp15
-rw-r--r--tools/examples/xmexample.vmx93
-rw-r--r--tools/examples/xmexample1 (renamed from tools/examples/xmdefaults)12
-rw-r--r--tools/examples/xmexample2 (renamed from tools/examples/xmexample)7
-rw-r--r--tools/examples/xmexample3 (renamed from tools/examples/xmnetbsd-example)53
-rw-r--r--tools/ioemu/Makefile19
-rw-r--r--tools/ioemu/font/vga.bitmap.h288
-rw-r--r--tools/ioemu/gui/Makefile12
-rw-r--r--tools/ioemu/gui/Makefile.in561
-rw-r--r--tools/ioemu/gui/bitmaps/cdromd.h34
-rw-r--r--tools/ioemu/gui/bitmaps/cdromd.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/configbutton.h16
-rw-r--r--tools/ioemu/gui/bitmaps/configbutton.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/copy.h18
-rw-r--r--tools/ioemu/gui/bitmaps/copy.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/floppya.h34
-rw-r--r--tools/ioemu/gui/bitmaps/floppya.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/floppyb.h34
-rw-r--r--tools/ioemu/gui/bitmaps/floppyb.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/mouse.h34
-rw-r--r--tools/ioemu/gui/bitmaps/mouse.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/paste.h18
-rw-r--r--tools/ioemu/gui/bitmaps/paste.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/power.h20
-rw-r--r--tools/ioemu/gui/bitmaps/power.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/reset.h20
-rw-r--r--tools/ioemu/gui/bitmaps/reset.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/snapshot.h20
-rw-r--r--tools/ioemu/gui/bitmaps/snapshot.xpm41
-rw-r--r--tools/ioemu/gui/bitmaps/userbutton.h19
-rw-r--r--tools/ioemu/gui/bitmaps/userbutton.xpm40
-rw-r--r--tools/ioemu/gui/gui.cc592
-rw-r--r--tools/ioemu/gui/gui.h352
-rw-r--r--tools/ioemu/gui/icon_bochs.h40
-rw-r--r--tools/ioemu/gui/icon_bochs.xpm45
-rw-r--r--tools/ioemu/gui/keymap.cc330
-rw-r--r--tools/ioemu/gui/keymap.h77
-rw-r--r--tools/ioemu/gui/keymaps/convertmap.pl14
-rw-r--r--tools/ioemu/gui/keymaps/sdl-pc-de.map222
-rw-r--r--tools/ioemu/gui/keymaps/sdl-pc-us.map211
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-be.map220
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-da.map247
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-de.map247
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-es.map217
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-fr.map218
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-it.map207
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-se.map278
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-uk.map209
-rw-r--r--tools/ioemu/gui/keymaps/x11-pc-us.map205
-rw-r--r--tools/ioemu/gui/nogui.cc336
-rw-r--r--tools/ioemu/gui/rfb.cc1508
-rw-r--r--tools/ioemu/gui/rfb.h35
-rw-r--r--tools/ioemu/gui/rfbproto.h675
-rw-r--r--tools/ioemu/gui/sdl.h1038
-rw-r--r--tools/ioemu/gui/sdlkeys.h257
-rw-r--r--tools/ioemu/gui/siminterface.cc1411
-rw-r--r--tools/ioemu/gui/siminterface.h1460
-rw-r--r--tools/ioemu/gui/svga.cc514
-rw-r--r--tools/ioemu/gui/term.cc843
-rw-r--r--tools/ioemu/gui/textconfig.cc995
-rw-r--r--tools/ioemu/gui/textconfig.h19
-rw-r--r--tools/ioemu/gui/x.cc1848
-rw-r--r--tools/ioemu/include/bochs.h771
-rw-r--r--tools/ioemu/include/bxversion.h7
-rw-r--r--tools/ioemu/include/config.h919
-rw-r--r--tools/ioemu/include/cpu/cpu.h116
-rw-r--r--tools/ioemu/include/extplugin.h51
-rw-r--r--tools/ioemu/include/instrument.h256
-rw-r--r--tools/ioemu/include/ltdl.h398
-rw-r--r--tools/ioemu/include/ltdlconf.h161
-rw-r--r--tools/ioemu/include/osdep.h176
-rw-r--r--tools/ioemu/include/pc_system.h226
-rw-r--r--tools/ioemu/include/plugin.h323
-rw-r--r--tools/ioemu/include/state_file.h61
-rw-r--r--tools/ioemu/iodev/Makefile15
-rw-r--r--tools/ioemu/iodev/aspi-win32.h210
-rw-r--r--tools/ioemu/iodev/biosdev.cc212
-rw-r--r--tools/ioemu/iodev/biosdev.h63
-rw-r--r--tools/ioemu/iodev/cdrom.cc1338
-rw-r--r--tools/ioemu/iodev/cdrom.h67
-rw-r--r--tools/ioemu/iodev/cdrom_beos.h10
-rw-r--r--tools/ioemu/iodev/cmos.cc824
-rw-r--r--tools/ioemu/iodev/cmos.h90
-rw-r--r--tools/ioemu/iodev/cpu.cc334
-rw-r--r--tools/ioemu/iodev/crc32.cc49
-rw-r--r--tools/ioemu/iodev/crc32.h25
-rw-r--r--tools/ioemu/iodev/devices.cc685
-rw-r--r--tools/ioemu/iodev/dma.cc825
-rw-r--r--tools/ioemu/iodev/dma.h114
-rw-r--r--tools/ioemu/iodev/eth.cc194
-rw-r--r--tools/ioemu/iodev/eth.h76
-rw-r--r--tools/ioemu/iodev/eth_arpback.cc214
-rw-r--r--tools/ioemu/iodev/eth_fbsd.cc385
-rw-r--r--tools/ioemu/iodev/eth_linux.cc286
-rw-r--r--tools/ioemu/iodev/eth_null.cc164
-rw-r--r--tools/ioemu/iodev/eth_packetmaker.cc184
-rw-r--r--tools/ioemu/iodev/eth_packetmaker.h135
-rw-r--r--tools/ioemu/iodev/eth_tap.cc370
-rw-r--r--tools/ioemu/iodev/eth_tuntap.cc401
-rw-r--r--tools/ioemu/iodev/extfpuirq.cc107
-rw-r--r--tools/ioemu/iodev/extfpuirq.h51
-rw-r--r--tools/ioemu/iodev/floppy.cc1633
-rw-r--r--tools/ioemu/iodev/floppy.h138
-rw-r--r--tools/ioemu/iodev/gameport.cc242
-rw-r--r--tools/ioemu/iodev/gameport.h63
-rw-r--r--tools/ioemu/iodev/guest2host.h77
-rw-r--r--tools/ioemu/iodev/harddrv.cc4880
-rw-r--r--tools/ioemu/iodev/harddrv.h765
-rw-r--r--tools/ioemu/iodev/ioapic.cc175
-rw-r--r--tools/ioemu/iodev/ioapic.h54
-rw-r--r--tools/ioemu/iodev/iodebug.cc354
-rw-r--r--tools/ioemu/iodev/iodebug.h35
-rw-r--r--tools/ioemu/iodev/iodev.h422
-rw-r--r--tools/ioemu/iodev/keyboard.cc1611
-rw-r--r--tools/ioemu/iodev/keyboard.h234
-rw-r--r--tools/ioemu/iodev/load32bitOShack.cc322
-rw-r--r--tools/ioemu/iodev/logio.cc631
-rw-r--r--tools/ioemu/iodev/main.cc4067
-rw-r--r--tools/ioemu/iodev/ne2k.cc1608
-rw-r--r--tools/ioemu/iodev/ne2k.h239
-rw-r--r--tools/ioemu/iodev/osdep.cc340
-rw-r--r--tools/ioemu/iodev/parallel.cc300
-rw-r--r--tools/ioemu/iodev/parallel.h78
-rw-r--r--tools/ioemu/iodev/pc_system.cc570
-rw-r--r--tools/ioemu/iodev/pci.cc467
-rw-r--r--tools/ioemu/iodev/pci.h90
-rw-r--r--tools/ioemu/iodev/pci2isa.cc294
-rw-r--r--tools/ioemu/iodev/pci2isa.h63
-rw-r--r--tools/ioemu/iodev/pciusb.cc668
-rw-r--r--tools/ioemu/iodev/pciusb.h195
-rw-r--r--tools/ioemu/iodev/pcivga.cc248
-rw-r--r--tools/ioemu/iodev/pcivga.h48
-rw-r--r--tools/ioemu/iodev/pic.cc887
-rw-r--r--tools/ioemu/iodev/pic.h97
-rw-r--r--tools/ioemu/iodev/pit.cc856
-rw-r--r--tools/ioemu/iodev/pit.h103
-rw-r--r--tools/ioemu/iodev/pit82c54.cc930
-rw-r--r--tools/ioemu/iodev/pit82c54.h143
-rw-r--r--tools/ioemu/iodev/pit_wrap.cc444
-rw-r--r--tools/ioemu/iodev/pit_wrap.h104
-rw-r--r--tools/ioemu/iodev/plugin.cc554
-rw-r--r--tools/ioemu/iodev/scancodes.cc770
-rw-r--r--tools/ioemu/iodev/scancodes.h31
-rw-r--r--tools/ioemu/iodev/scsi_commands.h418
-rw-r--r--tools/ioemu/iodev/scsidefs.h286
-rw-r--r--tools/ioemu/iodev/scsipt.h144
-rw-r--r--tools/ioemu/iodev/serial.cc1001
-rw-r--r--tools/ioemu/iodev/serial.h193
-rw-r--r--tools/ioemu/iodev/serial_raw.h23
-rw-r--r--tools/ioemu/iodev/slowdown_timer.cc182
-rw-r--r--tools/ioemu/iodev/slowdown_timer.h33
-rw-r--r--tools/ioemu/iodev/soundlnx.cc227
-rw-r--r--tools/ioemu/iodev/soundlnx.h69
-rw-r--r--tools/ioemu/iodev/soundwin.cc521
-rw-r--r--tools/ioemu/iodev/soundwin.h229
-rw-r--r--tools/ioemu/iodev/state_file.cc136
-rw-r--r--tools/ioemu/iodev/unmapped.cc305
-rw-r--r--tools/ioemu/iodev/unmapped.h64
-rw-r--r--tools/ioemu/iodev/vga.cc3116
-rw-r--r--tools/ioemu/iodev/vga.h300
-rw-r--r--tools/ioemu/iodev/virt_timer.cc552
-rw-r--r--tools/ioemu/iodev/virt_timer.h131
-rw-r--r--tools/ioemu/memory/Makefile12
-rw-r--r--tools/ioemu/memory/memory.cc450
-rw-r--r--tools/ioemu/memory/memory.h98
-rw-r--r--tools/ioemu/memory/misc_mem.cc440
-rw-r--r--tools/ioemu/mk/helix.mk6
-rw-r--r--tools/libxc/Makefile88
-rw-r--r--tools/libxc/linux_boot_params.h165
-rwxr-xr-xtools/libxc/plan9a.out.h28
-rw-r--r--tools/libxc/xc.h345
-rw-r--r--tools/libxc/xc_domain.c109
-rw-r--r--tools/libxc/xc_evtchn.c45
-rw-r--r--tools/libxc/xc_fbvtsched.c89
-rw-r--r--tools/libxc/xc_io.c12
-rw-r--r--tools/libxc/xc_io.h17
-rw-r--r--tools/libxc/xc_linux_build.c485
-rw-r--r--tools/libxc/xc_linux_restore.c217
-rw-r--r--tools/libxc/xc_linux_save.c434
-rw-r--r--tools/libxc/xc_misc.c28
-rw-r--r--tools/libxc/xc_netbsd_build.c706
-rwxr-xr-xtools/libxc/xc_plan9_build.c699
-rw-r--r--tools/libxc/xc_private.c306
-rw-r--r--tools/libxc/xc_private.h81
-rw-r--r--tools/libxc/xc_vmx_build.c802
-rw-r--r--tools/libxutil/Makefile77
-rw-r--r--tools/libxutil/debug.h72
-rw-r--r--tools/libxutil/enum.c (renamed from tools/xfrd/enum.c)0
-rw-r--r--tools/libxutil/enum.h (renamed from tools/xfrd/enum.h)0
-rw-r--r--tools/libxutil/hash_table.c (renamed from tools/xfrd/hash_table.c)0
-rw-r--r--tools/libxutil/hash_table.h (renamed from tools/xfrd/hash_table.h)0
-rw-r--r--tools/libxutil/iostream.c18
-rw-r--r--tools/libxutil/iostream.h30
-rw-r--r--tools/libxutil/kernel_stream.c8
-rw-r--r--tools/libxutil/lexis.c (renamed from tools/xfrd/lexis.c)1
-rw-r--r--tools/libxutil/lexis.h (renamed from tools/xfrd/lexis.h)1
-rw-r--r--tools/libxutil/socket_stream.c230
-rw-r--r--tools/libxutil/socket_stream.h53
-rw-r--r--tools/libxutil/string_stream.c2
-rw-r--r--tools/libxutil/string_stream.h2
-rw-r--r--tools/libxutil/sxpr.c (renamed from tools/xfrd/sxpr.c)6
-rw-r--r--tools/libxutil/sxpr.h (renamed from tools/xfrd/sxpr.h)6
-rw-r--r--tools/libxutil/sxpr_parser.c (renamed from tools/xfrd/sxpr_parser.c)53
-rw-r--r--tools/libxutil/sxpr_parser.h (renamed from tools/xfrd/sxpr_parser.h)1
-rw-r--r--tools/libxutil/sys_net.c4
-rw-r--r--tools/libxutil/util.c106
-rw-r--r--tools/libxutil/util.h28
-rw-r--r--tools/misc/Makefile41
-rw-r--r--tools/misc/miniterm/Makefile7
-rw-r--r--tools/misc/netfix3
-rw-r--r--tools/misc/p4perf.h559
-rwxr-xr-xtools/misc/xen-clone20
-rw-r--r--tools/misc/xen_cpuperf.c271
-rwxr-xr-xtools/misc/xencons28
-rw-r--r--tools/misc/xend55
-rw-r--r--tools/misc/xenperf.c104
-rwxr-xr-xtools/misc/xensv4
-rwxr-xr-xtools/misc/xensymoops (renamed from tools/misc/xensymoops.py)0
-rwxr-xr-xtools/misc/xm3
-rw-r--r--tools/python/Makefile13
-rw-r--r--tools/python/setup.py5
-rw-r--r--tools/python/xen/lowlevel/xc/xc.c319
-rw-r--r--tools/python/xen/lowlevel/xu/xu.c1005
-rw-r--r--tools/python/xen/sv/CreateDomain.py29
-rw-r--r--tools/python/xen/sv/Daemon.py6
-rwxr-xr-xtools/python/xen/sv/DomInfo.py64
-rwxr-xr-xtools/python/xen/sv/DomList.py49
-rwxr-xr-xtools/python/xen/sv/HTMLBase.py2
-rwxr-xr-xtools/python/xen/sv/Main.py43
-rw-r--r--tools/python/xen/sv/MigrateDomain.py72
-rwxr-xr-xtools/python/xen/sv/NodeInfo.py19
-rw-r--r--tools/python/xen/sv/RestoreDomain.py46
-rw-r--r--tools/python/xen/sv/SaveDomain.py59
-rw-r--r--tools/python/xen/sv/params.py4
-rwxr-xr-xtools/python/xen/sv/util.py8
-rw-r--r--tools/python/xen/util/console_client.py26
-rw-r--r--tools/python/xen/util/memmap.py41
-rw-r--r--tools/python/xen/xend/Blkctl.py45
-rw-r--r--tools/python/xen/xend/XendAsynchProtocol.py94
-rw-r--r--tools/python/xen/xend/XendClient.py312
-rw-r--r--tools/python/xen/xend/XendDmesg.py4
-rw-r--r--tools/python/xen/xend/XendDomain.py161
-rw-r--r--tools/python/xen/xend/XendDomainInfo.py668
-rw-r--r--tools/python/xen/xend/XendLogging.py15
-rw-r--r--tools/python/xen/xend/XendMigrate.py276
-rw-r--r--tools/python/xen/xend/XendNode.py6
-rw-r--r--tools/python/xen/xend/XendProtocol.py156
-rw-r--r--tools/python/xen/xend/XendRoot.py171
-rw-r--r--tools/python/xen/xend/XendVnet.py11
-rw-r--r--tools/python/xen/xend/encode.py4
-rw-r--r--tools/python/xen/xend/server/SrvBase.py24
-rw-r--r--tools/python/xen/xend/server/SrvDaemon.py472
-rw-r--r--tools/python/xen/xend/server/SrvDmesg.py7
-rw-r--r--tools/python/xen/xend/server/SrvDomain.py71
-rw-r--r--tools/python/xen/xend/server/SrvDomainDir.py25
-rw-r--r--tools/python/xen/xend/server/SrvNode.py6
-rw-r--r--tools/python/xen/xend/server/SrvServer.py4
-rw-r--r--tools/python/xen/xend/server/SrvUsbif.py249
-rwxr-xr-xtools/python/xen/xend/server/blkif.py822
-rwxr-xr-xtools/python/xen/xend/server/channel.py44
-rwxr-xr-xtools/python/xen/xend/server/console.py11
-rwxr-xr-xtools/python/xen/xend/server/controller.py424
-rw-r--r--tools/python/xen/xend/server/domain.py39
-rw-r--r--tools/python/xen/xend/server/messages.py281
-rwxr-xr-xtools/python/xen/xend/server/netif.py535
-rw-r--r--tools/python/xen/xend/server/params.py8
-rw-r--r--tools/python/xen/xend/server/usbif.py368
-rw-r--r--tools/python/xen/xend/sxp.py79
-rw-r--r--tools/python/xen/xend/util.py0
-rw-r--r--tools/python/xen/xm/create.py225
-rw-r--r--tools/python/xen/xm/help.py4
-rw-r--r--tools/python/xen/xm/main.py169
-rw-r--r--tools/python/xen/xm/migrate.py50
-rw-r--r--tools/python/xen/xm/opts.py154
-rw-r--r--tools/python/xen/xm/shutdown.py5
-rw-r--r--tools/python/xen/xm/sysrq.py39
-rwxr-xr-xtools/sv/Makefile65
-rw-r--r--tools/sv/images/destroy.pngbin0 -> 2408 bytes
-rw-r--r--tools/sv/images/small-destroy.pngbin0 -> 483 bytes
-rw-r--r--tools/sv/images/small-pause.pngbin0 -> 434 bytes
-rw-r--r--tools/sv/images/small-unpause.pngbin0 -> 500 bytes
-rwxr-xr-xtools/sv/inc/script.js7
-rw-r--r--tools/vnet/00README10
-rw-r--r--tools/vnet/INSTALL31
-rw-r--r--tools/vnet/Makefile51
-rw-r--r--tools/vnet/doc/vnet-module.txt50
-rw-r--r--tools/vnet/doc/vnet-xend.txt140
-rw-r--r--tools/vnet/examples/Makefile12
-rwxr-xr-xtools/vnet/examples/network-vnet218
-rw-r--r--tools/vnet/examples/vnet97.sxp3
-rw-r--r--tools/vnet/examples/vnet98.sxp3
-rw-r--r--tools/vnet/examples/vnet99.sxp3
-rw-r--r--tools/vnet/vnet-module/00README39
-rw-r--r--tools/vnet/vnet-module/Makefile67
-rw-r--r--tools/vnet/vnet-module/Makefile-2.497
-rw-r--r--tools/vnet/vnet-module/Makefile-2.651
-rw-r--r--tools/vnet/vnet-module/Makefile.ver49
-rw-r--r--tools/vnet/vnet-module/Makefile.vnet57
-rw-r--r--tools/vnet/vnet-module/esp.c863
-rw-r--r--tools/vnet/vnet-module/esp.h111
-rw-r--r--tools/vnet/vnet-module/etherip.c411
-rw-r--r--tools/vnet/vnet-module/etherip.h27
-rw-r--r--tools/vnet/vnet-module/if_etherip.h51
-rw-r--r--tools/vnet/vnet-module/if_varp.h53
-rw-r--r--tools/vnet/vnet-module/linux/pfkeyv2.h329
-rw-r--r--tools/vnet/vnet-module/random.c101
-rw-r--r--tools/vnet/vnet-module/random.h30
-rw-r--r--tools/vnet/vnet-module/sa.c670
-rw-r--r--tools/vnet/vnet-module/sa.h199
-rw-r--r--tools/vnet/vnet-module/sa_algorithm.c367
-rw-r--r--tools/vnet/vnet-module/sa_algorithm.h63
-rw-r--r--tools/vnet/vnet-module/skb_context.c92
-rw-r--r--tools/vnet/vnet-module/skb_context.h76
-rw-r--r--tools/vnet/vnet-module/skb_util.c515
-rw-r--r--tools/vnet/vnet-module/skb_util.h43
-rw-r--r--tools/vnet/vnet-module/tunnel.c228
-rw-r--r--tools/vnet/vnet-module/tunnel.h101
-rw-r--r--tools/vnet/vnet-module/varp.c1236
-rw-r--r--tools/vnet/vnet-module/varp.h144
-rw-r--r--tools/vnet/vnet-module/varp_socket.c639
-rw-r--r--tools/vnet/vnet-module/vif.c267
-rw-r--r--tools/vnet/vnet-module/vif.h55
-rw-r--r--tools/vnet/vnet-module/vnet.c767
-rw-r--r--tools/vnet/vnet-module/vnet.h88
-rw-r--r--tools/vnet/vnet-module/vnet_dev.c534
-rw-r--r--tools/vnet/vnet-module/vnet_dev.h31
-rw-r--r--tools/vnet/vnet-module/vnet_ioctl.c815
-rw-r--r--tools/vnet/vnet-module/vnet_ioctl.h25
-rw-r--r--tools/vnet/vnetd/Makefile102
-rw-r--r--tools/vnet/vnetd/connection.c167
-rw-r--r--tools/vnet/vnetd/connection.h51
-rw-r--r--tools/vnet/vnetd/marshal.c223
-rw-r--r--tools/vnet/vnetd/marshal.h58
-rw-r--r--tools/vnet/vnetd/select.c67
-rw-r--r--tools/vnet/vnetd/select.h32
-rw-r--r--tools/vnet/vnetd/timer.c154
-rw-r--r--tools/vnet/vnetd/timer.h39
-rw-r--r--tools/vnet/vnetd/vcache.c639
-rw-r--r--tools/vnet/vnetd/vcache.h141
-rw-r--r--tools/vnet/vnetd/vnetd.c1239
-rw-r--r--tools/vnet/vnetd/vnetd.h80
-rw-r--r--tools/x2d2/Makefile22
-rw-r--r--tools/x2d2/cntrl_con.c457
-rw-r--r--tools/x2d2/minixend.c939
-rw-r--r--tools/x2d2/minixend.h154
-rw-r--r--tools/x2d2/util.c132
-rw-r--r--tools/xcs/Makefile48
-rw-r--r--tools/xcs/bindings.c179
-rw-r--r--tools/xcs/connection.c157
-rw-r--r--tools/xcs/ctrl_interface.c269
-rw-r--r--tools/xcs/evtchn.c108
-rw-r--r--tools/xcs/xcs.c880
-rw-r--r--tools/xcs/xcs.h155
-rw-r--r--tools/xcs/xcs_proto.h101
-rw-r--r--tools/xcs/xcsdump.c182
-rw-r--r--tools/xentrace/Makefile24
-rw-r--r--tools/xentrace/xentrace.c65
-rw-r--r--tools/xfrd/Make.xfrd1
-rw-r--r--tools/xfrd/Makefile25
-rw-r--r--tools/xfrd/debug.h15
-rw-r--r--tools/xfrd/http.h50
-rw-r--r--tools/xfrd/xen_domain.c251
-rw-r--r--tools/xfrd/xen_domain.h11
-rw-r--r--tools/xfrd/xfrd.c268
-rw-r--r--tools/xfrd/xfrd.h1
417 files changed, 93402 insertions, 5534 deletions
diff --git a/tools/Makefile b/tools/Makefile
index e8cdd96672..4e590fd30b 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -1,5 +1,6 @@
all:
+ $(MAKE) -C check
$(MAKE) -C libxutil
$(MAKE) -C libxc
$(MAKE) -C misc
@@ -7,8 +8,11 @@ all:
$(MAKE) -C xentrace
$(MAKE) -C python
$(MAKE) -C xfrd
+ $(MAKE) -C xcs
+ $(MAKE) -C ioemu
-install: all
+install:
+ $(MAKE) -C check
$(MAKE) -C libxutil install
$(MAKE) -C libxc install
$(MAKE) -C misc install
@@ -17,10 +21,8 @@ install: all
$(MAKE) -C python install
$(MAKE) -C xfrd install
$(MAKE) -C sv install
-
-dist: $(TARGET)
- $(MAKE) prefix=`pwd`/../../install dist=yes install
-
+ $(MAKE) -C xcs install
+ $(MAKE) -C ioemu install
clean:
$(MAKE) -C libxutil clean
@@ -30,4 +32,5 @@ clean:
$(MAKE) -C xentrace clean
$(MAKE) -C python clean
$(MAKE) -C xfrd clean
-
+ $(MAKE) -C xcs clean
+ $(MAKE) -C ioemu clean
diff --git a/tools/Make.defs b/tools/Rules.mk
index 1b4d4fec7f..2666f43842 100644
--- a/tools/Make.defs
+++ b/tools/Rules.mk
@@ -1,6 +1,15 @@
# -*- mode: Makefile; -*-
-XEN_HYPERVISOR_IFS = $(XEN_ROOT)/xen/include/hypervisor-ifs
-XEN_LINUX_INCLUDE = $(XEN_ROOT)/linux-xen-sparse/include
+
XEN_XC = $(XEN_ROOT)/tools/python/xen/lowlevel/xc
XEN_LIBXC = $(XEN_ROOT)/tools/libxc
XEN_LIBXUTIL = $(XEN_ROOT)/tools/libxutil
+
+ifeq ($(TARGET_ARCH),x86_32)
+CFLAGS += -m32 -march=i686
+LDFLAGS += -m elf_i386
+endif
+
+ifeq ($(TARGET_ARCH),x86_64)
+CFLAGS += -m64
+LDFLAGS += -m elf_x86_64
+endif
diff --git a/tools/blktap/Makefile b/tools/blktap/Makefile
new file mode 100644
index 0000000000..389095e68c
--- /dev/null
+++ b/tools/blktap/Makefile
@@ -0,0 +1,100 @@
+MAJOR = 2.0
+MINOR = 0
+SONAME = libblktap.so.$(MAJOR)
+
+CC = gcc
+
+XEN_ROOT = ../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+INCLUDES +=
+
+SRCS :=
+SRCS += blktaplib.c
+
+CFLAGS += -Wall
+CFLAGS += -Werror
+CFLAGS += -Wno-unused
+#CFLAGS += -O3
+CFLAGS += -g3
+CFLAGS += -fno-strict-aliasing
+CFLAGS += -I $(XEN_LIBXC)
+CFLAGS += -I $(XEN_LIBXUTIL)
+CFLAGS += $(INCLUDES) -I.
+CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE
+# Get gcc to generate the dependencies for us.
+CFLAGS += -Wp,-MD,.$(@F).d
+DEPS = .*.d
+
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+
+LIB = libblktap.so libblktap.so.$(MAJOR) libblktap.so.$(MAJOR).$(MINOR)
+
+all: mk-symlinks blkdump blkcow blkimg blkcowimg blkgnbd blkcowgnbd blkaio
+ $(MAKE) $(LIB)
+
+LINUX_ROOT := $(wildcard $(XEN_ROOT)/linux-2.6.*-xen-sparse)
+mk-symlinks:
+ [ -e xen/linux ] || mkdir -p xen/linux
+ [ -e xen/io ] || mkdir -p xen/io
+ ( cd xen >/dev/null ; \
+ ln -sf ../$(XEN_ROOT)/xen/include/public/*.h . )
+ ( cd xen/io >/dev/null ; \
+ ln -sf ../../$(XEN_ROOT)/xen/include/public/io/*.h . )
+ ( cd xen/linux >/dev/null ; \
+ ln -sf ../../$(LINUX_ROOT)/include/asm-xen/linux-public/*.h . )
+
+install: all
+ mkdir -p $(prefix)/usr/lib
+ mkdir -p $(prefix)/usr/include
+ install -m0755 $(LIB) $(prefix)/usr/lib
+ ln -sf libblktap.so.$(MAJOR).$(MINOR) \
+ $(prefix)/usr/lib/libblktap.so.$(MAJOR)
+ ln -sf libblktap.so.$(MAJOR) $(prefix)/usr/lib/libblktap.so
+ install -m0644 blktaplib.h $(prefix)/usr/include
+
+clean:
+ rm -rf *.a *.so *.o *.rpm $(LIB) *~ $(DEPS) xen TAGS blkdump blkcow blkimg blkcowimg blkgnbd blkcowgnbd blkaio
+
+rpm: all
+ rm -rf staging
+ mkdir staging
+ mkdir staging/i386
+ rpmbuild --define "staging$$PWD/staging" --define '_builddir.' \
+ --define "_rpmdir$$PWD/staging" -bb rpm.spec
+ mv staging/i386/*.rpm .
+ rm -rf staging
+
+libblktap.so:
+ ln -sf libblktap.so.$(MAJOR) $@
+libblktap.so.$(MAJOR):
+ ln -sf libblktap.so.$(MAJOR).$(MINOR) $@
+libblktap.so.$(MAJOR).$(MINOR): $(OBJS)
+ $(CC) -Wl,-soname -Wl,$(SONAME) -shared -o $@ $^ -L../libxutil -lxutil -lz
+
+blkdump: $(LIB)
+ $(CC) $(CFLAGS) -o blkdump -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -L. -l blktap blkdump.c
+
+blkcowimg: $(LIB) blkcowimg.c blkcowlib.c blkimglib.c
+ $(CC) $(CFLAGS) -o blkcowimg -ldb -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -L. -l blktap blkcowimg.c blkimglib.c blkcowlib.c
+
+blkcow: $(LIB) blkcow.c blkcowlib.c
+ $(CC) $(CFLAGS) -o blkcow -ldb -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -L. -l blktap blkcow.c blkcowlib.c
+
+blkimg: $(LIB) blkimg.c blkimglib.c
+ $(CC) $(CFLAGS) -o blkimg -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -L. -l blktap blkimg.c blkimglib.c
+
+blkgnbd: $(LIB) blkgnbd.c blkgnbdlib.c
+ $(CC) $(CFLAGS) -o blkgnbd -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -L. -lblktap blkgnbd.c blkgnbdlib.c libgnbd/libgnbd.a
+
+blkcowgnbd: $(LIB) blkgnbd.c blkcowlib.c blkgnbdlib.c
+ $(CC) $(CFLAGS) -o blkcowgnbd -ldb -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -L. -lblktap blkcowgnbd.c blkgnbdlib.c blkcowlib.c libgnbd/libgnbd.a
+
+blkaio: $(LIB) blkaio.c blkaiolib.c
+ $(CC) $(CFLAGS) -o blkaio -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -L. -lblktap blkaio.c blkaiolib.c -laio -lpthread
+
+.PHONY: TAGS clean install mk-symlinks rpm
+TAGS:
+ etags -t $(SRCS) *.h
+
+-include $(DEPS)
diff --git a/tools/blktap/README b/tools/blktap/README
new file mode 100644
index 0000000000..cca9a28fd9
--- /dev/null
+++ b/tools/blktap/README
@@ -0,0 +1,149 @@
+Block Tap User-level Interfaces
+Andrew Warfield
+andrew.warfield@cl.cam.ac.uk
+February 8, 2005
+
+NOTE #1: The blktap is _experimental_ code. It works for me. Your
+mileage may vary. Don't use it for anything important. Please. ;)
+
+NOTE #2: All of the interfaces here are likely to change. This is all
+early code, and I am checking it in because others want to play with
+it. If you use it for anything, please let me know!
+
+Overview:
+---------
+
+This directory contains a library and set of example applications for
+the block tap device. The block tap hooks into the split block device
+interfaces above Xen allowing them to be extended. This extension can
+be done in userspace with the help of a library.
+
+The tap can be installed either as an interposition domain in between
+a frontend and backend driver pair, or as a terminating backend, in
+which case it is responsible for serving all requests itself.
+
+There are two reasons that you might want to use the tap,
+corresponding to these configurations:
+
+ 1. To examine or modify a stream of block requests while they are
+ in-flight (e.g. to encrypt data, or add data-driven watchpoints)
+
+ 2. To prototype a new backend driver, serving requests from the tap
+ rather than passing them along to the XenLinux blkback driver.
+ (e.g. to forward block requests to a remote host)
+
+
+Interface:
+----------
+
+At the moment, the tap interface is similar in spirit to that of the
+Linux netfilter. Requests are messages from a client (frontend)
+domain to a disk (backend) domain. Responses are messages travelling
+back, acknowledging the completion of a request. the library allows
+chains of functions to be attached to these events. In addition,
+hooks may be attached to handle control messages, which signify things
+like connections from new domains.
+
+At present the control messages especially expose a lot of the
+underlying driver interfaces. This may change in the future in order
+to simplify writing hooks.
+
+Here are the public interfaces:
+
+These allow hook functions to be chained:
+
+ void blktap_register_ctrl_hook(char *name, int (*ch)(control_msg_t *));
+ void blktap_register_request_hook(char *name, int (*rh)(blkif_request_t *));
+ void blktap_register_response_hook(char *name, int (*rh)(blkif_response_t *));
+
+This allows a response to be injected, in the case where a request has
+been removed using BLKTAP_STOLEN.
+
+ void blktap_inject_response(blkif_response_t *);
+
+These let you add file descriptors and handlers to the main poll loop:
+
+ int blktap_attach_poll(int fd, short events, int (*func)(int));
+ void blktap_detach_poll(int fd);
+
+This starts the main poll loop:
+
+ int blktap_listen(void);
+
+Example:
+--------
+
+blkimage.c uses an image on the local file system to serve requests to
+a domain. Here's what it looks like:
+
+---[blkimg.c]---
+
+/* blkimg.c
+ *
+ * file-backed disk.
+ */
+
+#include "blktaplib.h"
+#include "blkimglib.h"
+
+
+int main(int argc, char *argv[])
+{
+ image_init();
+
+ blktap_register_ctrl_hook("image_control", image_control);
+ blktap_register_request_hook("image_request", image_request);
+ blktap_listen();
+
+ return 0;
+}
+
+----------------
+
+All of the real work is in blkimglib.c, but this illustrates the
+actual tap interface well enough. image_control() will be called with
+all control messages. image_request() handles requests. As it reads
+from an on-disk image file, no requests are ever passed on to a
+backend, and so there will be no responses to process -- so there is
+nothing registered as a response hook.
+
+Other examples:
+---------------
+
+Here is a list of other examples in the directory:
+
+Things that terminate a block request stream:
+
+ blkimg - Use a image file/device to serve requests
+ blkgnbd - Use a remote gnbd server to serve requests
+ blkaio - Use libaio... (DOES NOT WORK)
+
+Things that don't:
+
+ blkdump - Print in-flight requests.
+ blkcow - Really inefficient copy-on-write disks using libdb to store
+ writes.
+
+There are examples of plugging these things together, for instance
+blkcowgnbd is a read-only gnbd device with copy-on-write to a local
+file.
+
+TODO:
+-----
+
+- Make session tracking work. At the moment these generally just handle a
+ single front-end client at a time.
+
+- Integrate with Xend. Need to cleanly pass a image identifier in the connect
+ message.
+
+- Make an asynchronous file-io terminator. The libaio attempt is
+ tragically stalled because mapped foreign pages make pfn_valid fail
+ (they are VM_IO), and so cannot be passed to aio as targets. A
+ better solution may be to tear the disk interfaces out of the real
+ backend and expose them somehow.
+
+- Make CoW suck less.
+
+- Do something more along the lines of dynamic linking for the
+ plugins, so thatthey don't all need a new main().
diff --git a/tools/blktap/blkaio.c b/tools/blktap/blkaio.c
new file mode 100644
index 0000000000..25495718a4
--- /dev/null
+++ b/tools/blktap/blkaio.c
@@ -0,0 +1,19 @@
+/* blkaio.c
+ *
+ * libaio-backed disk.
+ */
+
+#include "blktaplib.h"
+#include "blkaiolib.h"
+
+
+int main(int argc, char *argv[])
+{
+ aio_init();
+
+ blktap_register_ctrl_hook("aio_control", aio_control);
+ blktap_register_request_hook("aio_request", aio_request);
+ blktap_listen();
+
+ return 0;
+}
diff --git a/tools/blktap/blkaiolib.c b/tools/blktap/blkaiolib.c
new file mode 100644
index 0000000000..4538a9eb31
--- /dev/null
+++ b/tools/blktap/blkaiolib.c
@@ -0,0 +1,489 @@
+/* blkaiolib.c
+ *
+ * file/device image-backed block device -- using linux libaio.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent.
+ *
+ * NOTE: This doesn't work. Grrr.
+ */
+
+#define _GNU_SOURCE
+#define __USE_LARGEFILE64
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <db.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <errno.h>
+#include <libaio.h>
+#include <pthread.h>
+#include <time.h>
+#include "blktaplib.h"
+
+//#define TMP_IMAGE_FILE_NAME "/dev/sda1"
+#define TMP_IMAGE_FILE_NAME "fc3.image"
+
+#define MAX_DOMS 1024
+#define MAX_IMGNAME_LEN 255
+#define AMORFS_DEV 61440
+#define MAX_REQUESTS 64 /* must be synced with the blkif drivers. */
+#define MAX_SEGMENTS_PER_REQ 11
+#define SECTOR_SHIFT 9
+#define MAX_AIO_REQS (MAX_REQUESTS * MAX_SEGMENTS_PER_REQ)
+
+#if 1
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+#if 1
+#define ASSERT(_p) \
+ if ( !(_p) ) { printf("Assertion '%s' failed, line %d, file %s", #_p , \
+ __LINE__, __FILE__); *(int*)0=0; }
+#else
+#define ASSERT(_p) ((void)0)
+#endif
+
+char dbg_page[4096];
+
+typedef struct {
+ /* These need to turn into an array/rbtree for multi-disk support. */
+ int fd;
+ u64 fsid;
+ char imgname[MAX_IMGNAME_LEN];
+ blkif_vdev_t vdevice;
+} image_t;
+
+/* Note on pending_reqs: I assume all reqs are queued before they start to
+ * get filled. so count of 0 is an unused record.
+ */
+typedef struct {
+ blkif_request_t req;
+ int count;
+} pending_req_t;
+
+static pending_req_t pending_list[MAX_REQUESTS];
+image_t *images[MAX_DOMS];
+
+static io_context_t ctx;
+static struct iocb *iocb_free[MAX_AIO_REQS];
+static int iocb_free_count;
+
+/* ---[ Notification mecahnism ]--------------------------------------- */
+
+enum {
+ READ = 0,
+ WRITE = 1
+};
+
+static int aio_notify[2];
+static volatile int aio_listening = 0;
+
+static struct io_event aio_events[MAX_AIO_REQS];
+static int aio_event_count = 0;
+
+/* this is commented out in libaio.h for some reason. */
+extern int io_queue_wait(io_context_t ctx, struct timespec *timeout);
+
+static void *notifier_thread(void *arg)
+{
+ int ret;
+ int msg = 0x00feeb00;
+
+ printf("Notifier thread started.\n");
+ for (;;) {
+ //if ((aio_listening) && ((ret = io_queue_wait(ctx, 0)) == 0)) {
+ if ((aio_listening) &&
+ ((ret = io_getevents(ctx, 1, MAX_AIO_REQS, aio_events, 0)) > 0)) {
+ aio_event_count = ret;
+ printf("[Notifying! (%d)]\n", aio_event_count);
+ aio_listening = 0;
+ write(aio_notify[WRITE], &msg, sizeof(msg));
+ fsync(aio_notify[WRITE]);
+ } else {
+ if (aio_listening)
+ printf("[io_queue_wait error! %d]\n", errno);
+ usleep(1000); /* Not ready to read. */
+ }
+ }
+}
+
+/* -------------------------------------------------------------------- */
+
+int aio_control(control_msg_t *msg)
+{
+ domid_t domid;
+ DB *db;
+ int ret;
+
+ if (msg->type != CMSG_BLKIF_BE)
+ {
+ printf("***\nUNEXPECTED CTRL MSG MAJOR TYPE(%d)\n***\n", msg->type);
+ return 0;
+ }
+
+ switch(msg->subtype)
+ {
+ case CMSG_BLKIF_BE_CREATE:
+ if ( msg->length != sizeof(blkif_be_create_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_CREATE(d:%d,h:%d)\n",
+ ((blkif_be_create_t *)msg->msg)->domid,
+ ((blkif_be_create_t *)msg->msg)->blkif_handle);
+ domid = ((blkif_be_create_t *)msg->msg)->domid;
+ if (images[domid] != NULL) {
+ printf("attempt to connect from an existing dom!\n");
+ return 0;
+ }
+
+ images[domid] = (image_t *)malloc(sizeof(image_t));
+ if (images[domid] == NULL) {
+ printf("error allocating image record.\n");
+ return 0;
+ }
+
+ images[domid]->fd = -1;
+ images[domid]->fsid = 0;
+
+ printf("Image connected.\n");
+ break;
+
+ case CMSG_BLKIF_BE_DESTROY:
+ if ( msg->length != sizeof(blkif_be_destroy_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_DESTROY(d:%d,h:%d)\n",
+ ((blkif_be_destroy_t *)msg->msg)->domid,
+ ((blkif_be_destroy_t *)msg->msg)->blkif_handle);
+
+ domid = ((blkif_be_destroy_t *)msg->msg)->domid;
+ if (images[domid] != NULL) {
+ if (images[domid]->fd != -1)
+ close( images[domid]->fd );
+ free( images[domid] );
+ images[domid] = NULL;
+ }
+ break;
+ case CMSG_BLKIF_BE_VBD_GROW:
+ {
+ blkif_be_vbd_grow_t *grow;
+
+ if ( msg->length != sizeof(blkif_be_vbd_grow_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_GROW(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->domid,
+ ((blkif_be_vbd_grow_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_grow_t *)msg->msg)->vdevice);
+ printf(" Extent: sec_start: %llu sec_len: %llu, dev: %d\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_start,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_length,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.device);
+ grow = (blkif_be_vbd_grow_t *)msg->msg;
+ domid = grow->domid;
+ if (images[domid] == NULL) {
+ printf("VBD_GROW on unconnected domain!\n");
+ return 0;
+ }
+
+ if (grow->extent.device != AMORFS_DEV) {
+ printf("VBD_GROW on non-amorfs device!\n");
+ return 0;
+ }
+
+ /* TODO: config support for arbitrary image files/modes. */
+ sprintf(images[domid]->imgname, TMP_IMAGE_FILE_NAME);
+
+ images[domid]->fsid = grow->extent.sector_start;
+ images[domid]->vdevice = grow->vdevice;
+ images[domid]->fd = open(TMP_IMAGE_FILE_NAME,
+ O_RDWR | O_DIRECT | O_LARGEFILE);
+ if (images[domid]->fd < 0) {
+ printf("Couldn't open image file! %d\n", errno);
+ return 0;
+ }
+
+ printf("Image file opened. (%s)\n", images[domid]->imgname);
+ break;
+ }
+ }
+ return 0;
+parse_error:
+ printf("Bad control message!\n");
+ return 0;
+
+create_failed:
+ /* TODO: close the db ref. */
+ return 0;
+}
+
+int aio_request(blkif_request_t *req)
+{
+ int fd;
+ u64 sector;
+ char *spage, *dpage;
+ int ret, i, idx;
+ blkif_response_t *rsp;
+ domid_t dom = ID_TO_DOM(req->id);
+
+ if ((images[dom] == NULL) || (images[dom]->fd == -1)) {
+ printf("Data request for unknown domain!!! %d\n", dom);
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+ }
+
+ fd = images[dom]->fd;
+
+ switch (req->operation)
+ {
+ case BLKIF_OP_PROBE:
+ {
+ struct stat stat;
+ vdisk_t *img_info;
+
+
+ /* We expect one buffer only. */
+ if ( req->nr_segments != 1 )
+ goto err;
+
+ /* Make sure the buffer is page-sized. */
+ if ( (blkif_first_sect(req->frame_and_sects[0]) != 0) ||
+ (blkif_last_sect (req->frame_and_sects[0]) != 7) )
+ goto err;
+
+ /* loop for multiple images would start here. */
+
+ ret = fstat(fd, &stat);
+ if (ret != 0) {
+ printf("Couldn't stat image in PROBE!\n");
+ goto err;
+ }
+
+ img_info = (vdisk_t *)MMAP_VADDR(ID_TO_IDX(req->id), 0);
+ img_info[0].device = images[dom]->vdevice;
+ img_info[0].info = VDISK_TYPE_DISK | VDISK_FLAG_VIRT;
+ img_info[0].capacity = (stat.st_size >> SECTOR_SHIFT);
+
+ if (img_info[0].capacity == 0)
+ img_info[0].capacity = ((u64)1 << 63); // xend does this too.
+
+ DPRINTF("iPROBE! device: 0x%04x capacity: %llu\n", img_info[0].device,
+ img_info[0].capacity);
+
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = BLKIF_OP_PROBE;
+ rsp->status = 1; /* number of disks */
+
+ return BLKTAP_RESPOND;
+ }
+ case BLKIF_OP_WRITE:
+ {
+ unsigned long size;
+ struct iocb *io;
+ struct iocb *ioq[MAX_SEGMENTS_PER_REQ];
+
+ idx = ID_TO_IDX(req->id);
+ ASSERT(pending_list[idx].count == 0);
+ memcpy(&pending_list[idx].req, req, sizeof(*req));
+ pending_list[idx].count = req->nr_segments;
+
+ for (i = 0; i < req->nr_segments; i++) {
+
+ sector = req->sector_number + (8*i);
+
+ size = blkif_last_sect (req->frame_and_sects[i]) -
+ blkif_first_sect(req->frame_and_sects[i]) + 1;
+
+ DPRINTF("iWRITE: sec_nr: %10llu sec: %10llu (%1lu,%1lu) pos: %15lu\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << SECTOR_SHIFT));
+
+ spage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ spage += blkif_first_sect(req->frame_and_sects[i]) << SECTOR_SHIFT;
+
+ /*convert size and sector to byte offsets */
+ size <<= SECTOR_SHIFT;
+ sector <<= SECTOR_SHIFT;
+
+ io = iocb_free[--iocb_free_count];
+ io_prep_pwrite(io, fd, spage, size, sector);
+ io->data = (void *)idx;
+ ioq[i] = io;
+ }
+
+ ret = io_submit(ctx, req->nr_segments, ioq);
+ if (ret < 0)
+ printf("BADNESS: io_submit error! (%d)\n", errno);
+
+ pending_list[idx].count = req->nr_segments;
+
+ return BLKTAP_STOLEN;
+
+ }
+ case BLKIF_OP_READ:
+ {
+ unsigned long size;
+ struct iocb *io;
+ struct iocb *ioq[MAX_SEGMENTS_PER_REQ];
+
+ idx = ID_TO_IDX(req->id);
+ ASSERT(pending_list[idx].count == 0);
+ memcpy(&pending_list[idx].req, req, sizeof(*req));
+ pending_list[idx].count = req->nr_segments;
+
+ for (i = 0; i < req->nr_segments; i++) {
+
+ sector = req->sector_number + (8*i);
+
+ size = blkif_last_sect (req->frame_and_sects[i]) -
+ blkif_first_sect(req->frame_and_sects[i]) + 1;
+
+ dpage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ dpage += blkif_first_sect(req->frame_and_sects[i]) << SECTOR_SHIFT;
+
+
+ DPRINTF("iREAD : sec_nr: %10llu sec: %10llu (%1lu,%1lu) "
+ "pos: %15lu dpage: %p\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << SECTOR_SHIFT), dpage);
+
+ /*convert size and sector to byte offsets */
+ size <<= SECTOR_SHIFT;
+ sector <<= SECTOR_SHIFT;
+
+ io = iocb_free[--iocb_free_count];
+
+ io_prep_pread(io, fd, dpage, size, sector);
+ io->data = (void *)idx;
+
+ ioq[i] = io;
+ }
+
+ ret = io_submit(ctx, req->nr_segments, ioq);
+ if (ret < 0)
+ printf("BADNESS: io_submit error! (%d)\n", errno);
+
+
+ return BLKTAP_STOLEN;
+
+ }
+ }
+
+ printf("Unknown block operation!\n");
+err:
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+}
+
+
+int aio_pollhook(int fd)
+{
+ struct io_event *ep;
+ int n, ret, idx;
+ blkif_request_t *req;
+ blkif_response_t *rsp;
+
+ DPRINTF("aio_hook(): \n");
+
+ for (ep = aio_events; aio_event_count-- > 0; ep++) {
+ struct iocb *io = ep->obj;
+ idx = (int) ep->data;
+
+ if ((idx > MAX_REQUESTS-1) || (pending_list[idx].count == 0)){
+ printf("gnbd returned a bad cookie (%u)!\n", idx);
+ break;
+ }
+
+ if ((int)ep->res < 0) printf("aio request error! (%d,%d)\n",
+ (int)ep->res, (int)ep->res2);
+
+ pending_list[idx].count--;
+ iocb_free[iocb_free_count++] = io;
+
+ if (pending_list[idx].count == 0) {
+ blkif_request_t tmp = pending_list[idx].req;
+ rsp = (blkif_response_t *)&pending_list[idx].req;
+ rsp->id = tmp.id;
+ rsp->operation = tmp.operation;
+ rsp->status = BLKIF_RSP_OKAY;
+ blktap_inject_response(rsp);
+ }
+ }
+
+ printf("pollhook done!\n");
+
+ read(aio_notify[READ], &idx, sizeof(idx));
+ aio_listening = 1;
+
+ return 0;
+}
+
+/* the image library terminates the request stream. _resp is a noop. */
+int aio_response(blkif_response_t *rsp)
+{
+ return BLKTAP_PASS;
+}
+
+void aio_init(void)
+{
+ int i, rc;
+ pthread_t p;
+
+ for (i = 0; i < MAX_DOMS; i++)
+ images[i] = NULL;
+
+ for (i = 0; i < MAX_REQUESTS; i++)
+ pending_list[i].count = 0;
+
+ memset(&ctx, 0, sizeof(ctx));
+ rc = io_queue_init(MAX_AIO_REQS, &ctx);
+ if (rc != 0) {
+ printf("queue_init failed! (%d)\n", rc);
+ exit(0);
+ }
+
+ for (i=0; i<MAX_AIO_REQS; i++) {
+ if (!(iocb_free[i] = (struct iocb *)malloc(sizeof(struct iocb)))) {
+ printf("error allocating iocb array\n");
+ exit(0);
+ }
+ iocb_free_count = i;
+ }
+
+ rc = pipe(aio_notify);
+ if (rc != 0) {
+ printf("pipe failed! (%d)\n", errno);
+ exit(0);
+ }
+
+ rc = pthread_create(&p, NULL, notifier_thread, NULL);
+ if (rc != 0) {
+ printf("pthread_create failed! (%d)\n", errno);
+ exit(0);
+ }
+
+ aio_listening = 1;
+
+ blktap_attach_poll(aio_notify[READ], POLLIN, aio_pollhook);
+}
+
diff --git a/tools/blktap/blkaiolib.h b/tools/blktap/blkaiolib.h
new file mode 100644
index 0000000000..7e26daef7e
--- /dev/null
+++ b/tools/blktap/blkaiolib.h
@@ -0,0 +1,16 @@
+/* blkaiolib.h
+ *
+ * aio image-backed block device.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent.
+ */
+
+int aio_control(control_msg_t *msg);
+int aio_request(blkif_request_t *req);
+int aio_response(blkif_response_t *rsp); /* noop */
+void aio_init(void);
diff --git a/tools/blktap/blkcow.c b/tools/blktap/blkcow.c
new file mode 100644
index 0000000000..82f933589d
--- /dev/null
+++ b/tools/blktap/blkcow.c
@@ -0,0 +1,31 @@
+/* blkcow.c
+ *
+ * copy on write a block device. in a really inefficient way.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * This uses whatever backend the tap is attached to as the read-only
+ * underlay -- for the moment.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent,
+ * the cow plugin uses this to identify a unique overlay.
+ */
+
+#include "blktaplib.h"
+#include "blkcowlib.h"
+
+
+int main(int argc, char *argv[])
+{
+ cow_init();
+
+ blktap_register_ctrl_hook("cow_control", cow_control);
+ blktap_register_request_hook("cow_request", cow_request);
+ blktap_register_response_hook("cow_response", cow_response);
+ blktap_listen();
+
+ return 0;
+}
diff --git a/tools/blktap/blkcowgnbd.c b/tools/blktap/blkcowgnbd.c
new file mode 100644
index 0000000000..81f9bad502
--- /dev/null
+++ b/tools/blktap/blkcowgnbd.c
@@ -0,0 +1,24 @@
+/* blkcowgnbd.c
+ *
+ * gnbd-backed cow.
+ */
+
+#include "blktaplib.h"
+#include "blkcowlib.h"
+#include "blkgnbdlib.h"
+
+
+int main(int argc, char *argv[])
+{
+ cow_init();
+ gnbd_init();
+
+ blktap_register_ctrl_hook("cow_control", cow_control);
+ blktap_register_ctrl_hook("gnbd_control", gnbd_control);
+ blktap_register_request_hook("cow_request", cow_request);
+ blktap_register_request_hook("gnbd_request", gnbd_request);
+ blktap_register_response_hook("cow_response", cow_response);
+ blktap_listen();
+
+ return 0;
+}
diff --git a/tools/blktap/blkcowimg.c b/tools/blktap/blkcowimg.c
new file mode 100644
index 0000000000..40aa1f8f16
--- /dev/null
+++ b/tools/blktap/blkcowimg.c
@@ -0,0 +1,24 @@
+/* blkcowimg.c
+ *
+ * file-backed cow.
+ */
+
+#include "blktaplib.h"
+#include "blkcowlib.h"
+#include "blkimglib.h"
+
+
+int main(int argc, char *argv[])
+{
+ cow_init();
+ image_init();
+
+ blktap_register_ctrl_hook("cow_control", cow_control);
+ blktap_register_ctrl_hook("image_control", image_control);
+ blktap_register_request_hook("cow_request", cow_request);
+ blktap_register_request_hook("image_request", image_request);
+ blktap_register_response_hook("cow_response", cow_response);
+ blktap_listen();
+
+ return 0;
+}
diff --git a/tools/blktap/blkcowlib.c b/tools/blktap/blkcowlib.c
new file mode 100644
index 0000000000..3518b4fa74
--- /dev/null
+++ b/tools/blktap/blkcowlib.c
@@ -0,0 +1,380 @@
+/* blkcowlib.c
+ *
+ * copy on write a block device. in a really inefficient way.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * This uses whatever backend the tap is attached to as the read-only
+ * underlay -- for the moment.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent,
+ * the cow plugin uses this to identify a unique overlay.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <db.h>
+#include "blktaplib.h"
+
+#define MAX_DOMS 1024
+#define MAX_DBNAME_LEN 255
+#define AMORFS_DEV 61440
+#define MAX_REQUESTS 64 /* must be synced with the blkif drivers. */
+
+#if 0
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+/* Berkeley db has different params for open() after 4.1 */
+#ifndef DB_VERSION_MAJOR
+# define DB_VERSION_MAJOR 1
+#endif /* DB_VERSION_MAJOR */
+#ifndef DB_VERSION_MINOR
+# define DB_VERSION_MINOR 0
+#endif /* DB_VERSION_MINOR */
+
+typedef struct {
+ DB *db;
+ u64 fsid;
+ char dbname[MAX_DBNAME_LEN];
+} cow_t;
+
+cow_t *cows[MAX_DOMS];
+blkif_request_t *reread_list[MAX_REQUESTS];
+
+int cow_control(control_msg_t *msg)
+{
+ domid_t domid;
+ DB *db;
+ int ret;
+
+ if (msg->type != CMSG_BLKIF_BE)
+ {
+ printf("***\nUNEXPECTED CTRL MSG MAJOR TYPE(%d)\n***\n", msg->type);
+ return 0;
+ }
+
+ switch(msg->subtype)
+ {
+ case CMSG_BLKIF_BE_CREATE:
+ if ( msg->length != sizeof(blkif_be_create_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_CREATE(d:%d,h:%d)\n",
+ ((blkif_be_create_t *)msg->msg)->domid,
+ ((blkif_be_create_t *)msg->msg)->blkif_handle);
+ domid = ((blkif_be_create_t *)msg->msg)->domid;
+ if (cows[domid] != NULL) {
+ printf("attempt to connect from an existing dom!\n");
+ return 0;
+ }
+
+ cows[domid] = (cow_t *)malloc(sizeof(cow_t));
+ if (cows[domid] == NULL) {
+ printf("error allocating cow.\n");
+ return 0;
+ }
+
+ cows[domid]->db = NULL;
+ cows[domid]->fsid = 0;
+
+ printf("COW connected.\n");
+ break;
+
+ case CMSG_BLKIF_BE_DESTROY:
+ if ( msg->length != sizeof(blkif_be_destroy_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_DESTROY(d:%d,h:%d)\n",
+ ((blkif_be_destroy_t *)msg->msg)->domid,
+ ((blkif_be_destroy_t *)msg->msg)->blkif_handle);
+
+ domid = ((blkif_be_destroy_t *)msg->msg)->domid;
+ if (cows[domid] != NULL) {
+ if (cows[domid]->db != NULL)
+ cows[domid]->db->close(cows[domid]->db, 0);
+ free(cows[domid]);
+ cows[domid] = NULL;
+ }
+ break;
+ case CMSG_BLKIF_BE_VBD_GROW:
+ {
+ blkif_be_vbd_grow_t *grow;
+
+ if ( msg->length != sizeof(blkif_be_vbd_grow_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_GROW(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->domid,
+ ((blkif_be_vbd_grow_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_grow_t *)msg->msg)->vdevice);
+ printf(" Extent: sec_start: %llu sec_len: %llu, dev: %d\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_start,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_length,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.device);
+ grow = (blkif_be_vbd_grow_t *)msg->msg;
+ domid = grow->domid;
+ if (cows[domid] == NULL) {
+ printf("VBD_GROW on unconnected domain!\n");
+ return 0;
+ }
+
+ if (grow->extent.device != AMORFS_DEV) {
+ printf("VBD_GROW on non-amorfs device!\n");
+ return 0;
+ }
+
+ sprintf(&cows[domid]->dbname[0], "%020llu.db",
+ grow->extent.sector_start);
+
+ cows[domid]->fsid = grow->extent.sector_start;
+
+ if ((ret = db_create(&db, NULL, 0)) != 0) {
+ fprintf(stderr, "db_create: %s\n", db_strerror(ret));
+ return 0;
+ }
+
+
+#if DB_VERSION_MAJOR < 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR < 1)
+
+ if ((ret = db->open( db, cows[domid]->dbname, NULL, DB_BTREE,
+ DB_CREATE, 0664)) != 0) {
+
+#else /* DB_VERSION >= 4.1 */
+
+ if ((ret = db->open( db, NULL, cows[domid]->dbname, NULL, DB_BTREE,
+ DB_CREATE, 0664)) != 0) {
+
+#endif /* DB_VERSION < 4.1 */
+
+ db->err(db, ret, "%s", cows[domid]->dbname);
+ goto create_failed;
+ }
+ cows[domid]->db = db;
+ printf("Overlay db opened. (%s)\n", cows[domid]->dbname);
+ break;
+ }
+ }
+ return 0;
+parse_error:
+ printf("Bad control message!\n");
+ return 0;
+
+create_failed:
+ /* TODO: close the db ref. */
+ return 0;
+}
+
+int cow_request(blkif_request_t *req)
+{
+ DB *db;
+ DBT key, data;
+ u64 sector;
+ char *spage, *dpage;
+ int ret, i, idx;
+ blkif_response_t *rsp;
+ domid_t dom = ID_TO_DOM(req->id);
+
+ if ((cows[dom] == NULL) || (cows[dom]->db == NULL)) {
+ printf("Data request for unknown domain!!! %d\n", dom);
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+ }
+
+ db = cows[dom]->db;
+
+ switch (req->operation)
+ {
+ case BLKIF_OP_PROBE:
+/* debug -- delete */
+idx = ID_TO_IDX(req->id);
+reread_list[idx] = (blkif_request_t *)malloc(sizeof(*req));
+memcpy(reread_list[idx], req, sizeof(*req));
+ return BLKTAP_PASS;
+
+ case BLKIF_OP_WRITE:
+ for (i = 0; i < req->nr_segments; i++) {
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ sector = req->sector_number + (8*i);
+ key.data = &sector;
+ key.size = sizeof(sector);
+
+ spage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ data.data = spage;
+ data.size = PAGE_SIZE;
+
+
+ DPRINTF("cWRITE: sec_nr: %10llu sec: %10llu (%1lu,%1lu) pos: %15lu\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << 9));
+
+ if ((ret = db->put(db, NULL, &key, &data, 0)) == 0)
+ DPRINTF("db: %lld: key stored.\n", *((u64 *)key.data));
+ else {
+ db->err(db, ret, "DB->put");
+ goto err;
+ }
+ }
+
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = BLKIF_OP_WRITE;
+ rsp->status = BLKIF_RSP_OKAY;
+
+ return BLKTAP_RESPOND;
+
+ case BLKIF_OP_READ:
+ for (i = 0; i < req->nr_segments; i++) {
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ sector = req->sector_number + (8*i);
+ key.data = &sector;
+ key.size = sizeof(sector);
+
+ DPRINTF("cREAD: sec_nr: %10llu sec: %10llu (%1lu,%1lu) pos: %15lu\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << 9));
+
+ if ((ret = db->get(db, NULL, &key, &data, 0)) == 0) {
+ DPRINTF("db: %llu: key retrieved (req).\n",
+ *((u64 *)key.data));
+
+ dpage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ spage = data.data;
+ memcpy(dpage, spage, PAGE_SIZE);
+
+ } else if (ret == DB_NOTFOUND) {
+ idx = ID_TO_IDX(req->id);
+ if (idx > MAX_REQUESTS) {
+ printf("Bad index!\n");
+ goto err;
+ }
+ if (reread_list[idx] != NULL) {
+ printf("Dupe index!\n");
+ goto err;
+ }
+ reread_list[idx] = (blkif_request_t *)malloc(sizeof(*req));
+ memcpy(reread_list[idx], req, sizeof(*req));
+ return BLKTAP_PASS;
+ } else {
+ db->err(db, ret, "DB->get");
+ goto err;
+ }
+ }
+
+
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = BLKIF_OP_READ;
+ rsp->status = BLKIF_RSP_OKAY;
+ return BLKTAP_RESPOND;
+ }
+
+ printf("Unknow block operation!\n");
+ return BLKTAP_PASS;
+err:
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+}
+
+int cow_response(blkif_response_t *rsp)
+{
+ blkif_request_t *req;
+ int i, ret;
+ DB *db;
+ DBT key, data;
+ u64 sector;
+ char *spage, *dpage;
+ int idx = ID_TO_IDX(rsp->id);
+ domid_t dom;
+
+ /* don't touch erroring responses. */
+ if (rsp->status == BLKIF_RSP_ERROR)
+ return BLKTAP_PASS;
+
+ if ((rsp->operation == BLKIF_OP_READ) && (reread_list[idx] != NULL))
+ {
+ req = reread_list[idx];
+ dom = ID_TO_DOM(req->id);
+
+ if ((cows[dom] == NULL) || (cows[dom]->db == NULL)) {
+ printf("Response from unknown domain!!! Very badness! %d\n", dom);
+ return BLKTAP_PASS;
+ }
+
+ db = cows[dom]->db;
+
+ for (i = 0; i < req->nr_segments; i++) {
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ sector = req->sector_number + (8*i);
+ key.data = &sector;
+ key.size = sizeof(sector);
+
+ if ((ret = db->get(db, NULL, &key, &data, 0)) == 0) {
+ printf("db: %llu: key retrieved (rsp).\n",
+ *((u64 *)key.data));
+
+ dpage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ spage = data.data;
+ memcpy(dpage, spage, PAGE_SIZE);
+
+ } else if (ret == DB_NOTFOUND) {
+ continue; /* We read this from disk. */
+ } else {
+ db->err(db, ret, "DB->get");
+ goto err;
+ }
+ }
+ free(reread_list[idx]);
+ reread_list[idx] = NULL;
+ }
+
+ if (rsp->operation == BLKIF_OP_PROBE) {
+
+ vdisk_t *img_info;
+
+ req = reread_list[idx];
+ img_info = (vdisk_t *)(char *)MMAP_VADDR(ID_TO_IDX(req->id), 0);
+ for (i =0; i < rsp->status; i++)
+ printf("PROBE (%d) device: 0x%04x capacity: %llu, info: 0x%04x\n",
+ i,
+ img_info[0].device,
+ img_info[0].capacity,
+ img_info[0].info);
+ free(reread_list[idx]);
+ reread_list[idx] = NULL;
+ }
+
+err:
+ return BLKTAP_PASS;
+}
+
+void cow_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_DOMS; i++)
+ cows[i] = NULL;
+
+ for (i = 0; i < MAX_REQUESTS; i++)
+ reread_list[MAX_REQUESTS] = NULL;
+}
+
diff --git a/tools/blktap/blkcowlib.h b/tools/blktap/blkcowlib.h
new file mode 100644
index 0000000000..e6bd7a5898
--- /dev/null
+++ b/tools/blktap/blkcowlib.h
@@ -0,0 +1,14 @@
+/* blkcowlib.h
+ *
+ * copy on write a block device. in a really inefficient way.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * public interfaces to the CoW tap.
+ *
+ */
+
+int cow_control (control_msg_t *msg);
+int cow_request (blkif_request_t *req);
+int cow_response (blkif_response_t *rsp);
+void cow_init (void);
diff --git a/tools/blktap/blkdump.c b/tools/blktap/blkdump.c
new file mode 100644
index 0000000000..f7cde9d89a
--- /dev/null
+++ b/tools/blktap/blkdump.c
@@ -0,0 +1,151 @@
+/* blkdump.c
+ *
+ * show a running trace of block requests as they fly by.
+ *
+ * (c) 2004 Andrew Warfield.
+ */
+
+#include <stdio.h>
+#include "blktaplib.h"
+
+int control_print(control_msg_t *msg)
+{
+ if (msg->type != CMSG_BLKIF_BE)
+ {
+ printf("***\nUNEXPECTED CTRL MSG MAJOR TYPE(%d)\n***\n", msg->type);
+ return 0;
+ }
+
+ switch(msg->subtype)
+ {
+ case CMSG_BLKIF_BE_CREATE:
+ if ( msg->length != sizeof(blkif_be_create_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_CREATE(d:%d,h:%d)\n",
+ ((blkif_be_create_t *)msg->msg)->domid,
+ ((blkif_be_create_t *)msg->msg)->blkif_handle);
+ break;
+ case CMSG_BLKIF_BE_DESTROY:
+ if ( msg->length != sizeof(blkif_be_destroy_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_DESTROY(d:%d,h:%d)\n",
+ ((blkif_be_destroy_t *)msg->msg)->domid,
+ ((blkif_be_destroy_t *)msg->msg)->blkif_handle);
+ break;
+ case CMSG_BLKIF_BE_CONNECT:
+ if ( msg->length != sizeof(blkif_be_connect_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_CONNECT(d:%d,h:%d)\n",
+ ((blkif_be_connect_t *)msg->msg)->domid,
+ ((blkif_be_connect_t *)msg->msg)->blkif_handle);
+ break;
+ case CMSG_BLKIF_BE_DISCONNECT:
+ if ( msg->length != sizeof(blkif_be_disconnect_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_DISCONNECT(d:%d,h:%d)\n",
+ ((blkif_be_disconnect_t *)msg->msg)->domid,
+ ((blkif_be_disconnect_t *)msg->msg)->blkif_handle);
+ break;
+ case CMSG_BLKIF_BE_VBD_CREATE:
+ if ( msg->length != sizeof(blkif_be_vbd_create_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_CREATE(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_create_t *)msg->msg)->domid,
+ ((blkif_be_vbd_create_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_create_t *)msg->msg)->vdevice);
+ break;
+ case CMSG_BLKIF_BE_VBD_DESTROY:
+ if ( msg->length != sizeof(blkif_be_vbd_destroy_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_DESTROY(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_destroy_t *)msg->msg)->domid,
+ ((blkif_be_vbd_destroy_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_destroy_t *)msg->msg)->vdevice);
+ break;
+ case CMSG_BLKIF_BE_VBD_GROW:
+ if ( msg->length != sizeof(blkif_be_vbd_grow_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_GROW(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->domid,
+ ((blkif_be_vbd_grow_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_grow_t *)msg->msg)->vdevice);
+ printf(" Extent: sec_start: %llu sec_len: %llu, dev: %d\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_start,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_length,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.device);
+ break;
+ case CMSG_BLKIF_BE_VBD_SHRINK:
+ if ( msg->length != sizeof(blkif_be_vbd_shrink_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_SHRINK(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_shrink_t *)msg->msg)->domid,
+ ((blkif_be_vbd_shrink_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_shrink_t *)msg->msg)->vdevice);
+ break;
+ default:
+ goto parse_error;
+ }
+
+ return 0;
+
+parse_error:
+ printf("[CONTROL_MSG] Bad message type or length!\n");
+ return 0;
+}
+
+int request_print(blkif_request_t *req)
+{
+ int i;
+ unsigned long fas;
+
+ if ( req->operation == BLKIF_OP_PROBE ) {
+ printf("[%2u:%2u<%s]\n", ID_TO_DOM(req->id), ID_TO_IDX(req->id),
+ blkif_op_name[req->operation]);
+ return BLKTAP_PASS;
+ } else {
+ printf("[%2u:%2u<%5s] (nr_segs: %03u, dev: %03u, %010llu)\n",
+ ID_TO_DOM(req->id), ID_TO_IDX(req->id),
+ blkif_op_name[req->operation],
+ req->nr_segments, req->device,
+ req->sector_number);
+
+
+ for (i=0; i < req->nr_segments; i++) {
+ fas = req->frame_and_sects[i];
+ printf(" (pf: 0x%8lx start: %lu stop: %lu)\n",
+ (fas & PAGE_MASK),
+ blkif_first_sect(fas),
+ blkif_last_sect(fas)
+ );
+ }
+
+ }
+
+ return BLKTAP_PASS;
+}
+
+int response_print(blkif_response_t *rsp)
+{
+ if ( rsp->operation == BLKIF_OP_PROBE ) {
+ printf("[%2u:%2u>%s]\n", ID_TO_DOM(rsp->id), ID_TO_IDX(rsp->id),
+ blkif_op_name[rsp->operation]);
+ return BLKTAP_PASS;
+ } else {
+ printf("[%2u:%2u>%5s] (status: %d)\n",
+ ID_TO_DOM(rsp->id), ID_TO_IDX(rsp->id),
+ blkif_op_name[rsp->operation],
+ rsp->status);
+
+ }
+ return BLKTAP_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ blktap_register_ctrl_hook("control_print", control_print);
+ blktap_register_request_hook("request_print", request_print);
+ blktap_register_response_hook("response_print", response_print);
+ blktap_listen();
+
+ return 0;
+}
diff --git a/tools/blktap/blkgnbd.c b/tools/blktap/blkgnbd.c
new file mode 100644
index 0000000000..6a6bd67285
--- /dev/null
+++ b/tools/blktap/blkgnbd.c
@@ -0,0 +1,19 @@
+/* blkgnbd.c
+ *
+ * gnbd-backed disk.
+ */
+
+#include "blktaplib.h"
+#include "blkgnbdlib.h"
+
+
+int main(int argc, char *argv[])
+{
+ gnbd_init();
+
+ blktap_register_ctrl_hook("gnbd_control", gnbd_control);
+ blktap_register_request_hook("gnbd_request", gnbd_request);
+ blktap_listen();
+
+ return 0;
+}
diff --git a/tools/blktap/blkgnbdlib.c b/tools/blktap/blkgnbdlib.c
new file mode 100644
index 0000000000..6eeb49c853
--- /dev/null
+++ b/tools/blktap/blkgnbdlib.c
@@ -0,0 +1,471 @@
+/* blkgnbdlib.c
+ *
+ * gnbd image-backed block device.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <db.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/poll.h>
+#include "blktaplib.h"
+#include "libgnbd/libgnbd.h"
+
+#define GNBD_SERVER "skirmish.cl.cam.ac.uk"
+#define GNBD_CLIENT "pengi-0.xeno.cl.cam.ac.uk"
+#define GNBD_MOUNT "fc2_akw27"
+#define GNBD_PORT 0x38e7
+
+#define MAX_DOMS 1024
+#define MAX_IMGNAME_LEN 255
+#define AMORFS_DEV 61440
+#define MAX_REQUESTS 64 /* must be synced with the blkif drivers. */
+#define SECTOR_SHIFT 9
+
+#if 0
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+#if 1
+#define ASSERT(_p) \
+ if ( !(_p) ) { printf("Assertion '%s' failed, line %d, file %s", #_p , \
+ __LINE__, __FILE__); *(int*)0=0; }
+#else
+#define ASSERT(_p) ((void)0)
+#endif
+
+#define GH_DISCONNECTED 0
+#define GH_PROBEWAITING 1
+#define GH_CONNECTED 2
+
+typedef struct {
+ /* These need to turn into an array/rbtree for multi-disk support. */
+ struct gnbd_handle *gh;
+ int gh_state;
+ int probe_idx; /* This really needs cleaning up after hotos. */
+ int fd;
+ u64 fsid;
+ char gnbdname[MAX_IMGNAME_LEN];
+ blkif_vdev_t vdevice;
+} gnbd_t;
+
+/* Note on pending_reqs: I assume all reqs are queued before they start to
+ * get filled. so count of 0 is an unused record.
+ */
+typedef struct {
+ blkif_request_t req;
+ int count;
+} pending_req_t;
+
+static gnbd_t *gnbds[MAX_DOMS];
+static pending_req_t pending_list[MAX_REQUESTS];
+static int pending_count = 0; /* debugging */
+
+
+gnbd_t *get_gnbd_by_fd(int fd)
+{
+ /* this is a linear scan for the moment. nees to be cleaned up for
+ multi-disk support. */
+
+ int i;
+
+ for (i=0; i< MAX_DOMS; i++)
+ if ((gnbds[i] != NULL) && (gnbds[i]->fd == fd))
+ return gnbds[i];
+
+ return NULL;
+}
+
+int gnbd_pollhook(int fd);
+
+int gnbd_control(control_msg_t *msg)
+{
+ domid_t domid;
+ DB *db;
+ int ret;
+
+ if (msg->type != CMSG_BLKIF_BE)
+ {
+ printf("***\nUNEXPECTED CTRL MSG MAJOR TYPE(%d)\n***\n", msg->type);
+ return 0;
+ }
+
+ switch(msg->subtype)
+ {
+ case CMSG_BLKIF_BE_CREATE:
+ if ( msg->length != sizeof(blkif_be_create_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_CREATE(d:%d,h:%d)\n",
+ ((blkif_be_create_t *)msg->msg)->domid,
+ ((blkif_be_create_t *)msg->msg)->blkif_handle);
+ domid = ((blkif_be_create_t *)msg->msg)->domid;
+ if (gnbds[domid] != NULL) {
+ printf("attempt to connect from an existing dom!\n");
+ return 0;
+ }
+
+ gnbds[domid] = (gnbd_t *)malloc(sizeof(gnbd_t));
+ if (gnbds[domid] == NULL) {
+ printf("error allocating gnbd record.\n");
+ return 0;
+ }
+
+ gnbds[domid]->gh = NULL;
+ gnbds[domid]->fsid = 0;
+
+ break;
+
+ case CMSG_BLKIF_BE_DESTROY:
+ if ( msg->length != sizeof(blkif_be_destroy_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_DESTROY(d:%d,h:%d)\n",
+ ((blkif_be_destroy_t *)msg->msg)->domid,
+ ((blkif_be_destroy_t *)msg->msg)->blkif_handle);
+
+ domid = ((blkif_be_destroy_t *)msg->msg)->domid;
+ if (gnbds[domid] != NULL) {
+ if (gnbds[domid]->gh != NULL) {
+ blktap_detach_poll(gnbds[domid]->fd);
+ free(gnbds[domid]->gh); /* XXX: Need a gnbd close call! */;
+ }
+ free( gnbds[domid] );
+ gnbds[domid] = NULL;
+ }
+ break;
+ case CMSG_BLKIF_BE_VBD_GROW:
+ {
+ blkif_be_vbd_grow_t *grow;
+
+ if ( msg->length != sizeof(blkif_be_vbd_grow_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_GROW(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->domid,
+ ((blkif_be_vbd_grow_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_grow_t *)msg->msg)->vdevice);
+ printf(" Extent: sec_start: %llu sec_len: %llu, dev: %d\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_start,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_length,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.device);
+ grow = (blkif_be_vbd_grow_t *)msg->msg;
+ domid = grow->domid;
+ if (gnbds[domid] == NULL) {
+ printf("VBD_GROW on unconnected domain!\n");
+ return 0;
+ }
+
+ if (grow->extent.device != AMORFS_DEV) {
+ printf("VBD_GROW on non-amorfs device!\n");
+ return 0;
+ }
+
+ /* TODO: config support for arbitrary gnbd files/modes. */
+ sprintf(gnbds[domid]->gnbdname, GNBD_MOUNT);
+
+ gnbds[domid]->fsid = grow->extent.sector_start;
+ gnbds[domid]->vdevice = grow->vdevice;
+ gnbds[domid]->gh_state = GH_DISCONNECTED;
+ gnbds[domid]->gh = gnbd_setup(GNBD_SERVER, GNBD_PORT,
+ gnbds[domid]->gnbdname, GNBD_CLIENT);
+ if (gnbds[domid]->gh == NULL) {
+ printf("Couldn't connect to gnbd mount!!\n");
+ return 0;
+ }
+ gnbds[domid]->fd = gnbd_fd(gnbds[domid]->gh);
+ blktap_attach_poll(gnbds[domid]->fd, POLLIN, gnbd_pollhook);
+
+ printf("gnbd mount connected. (%s)\n", gnbds[domid]->gnbdname);
+ break;
+ }
+ }
+ return 0;
+parse_error:
+ printf("Bad control message!\n");
+ return 0;
+
+create_failed:
+ /* TODO: close the db ref. */
+ return 0;
+}
+
+static int gnbd_blkif_probe(blkif_request_t *req, gnbd_t *gnbd)
+{
+ int fd;
+ struct stat stat;
+ vdisk_t *gnbd_info;
+ blkif_response_t *rsp;
+
+ /* We expect one buffer only. */
+ if ( req->nr_segments != 1 )
+ goto err;
+
+ /* Make sure the buffer is page-sized. */
+ if ( (blkif_first_sect(req->frame_and_sects[0]) != 0) ||
+ (blkif_last_sect (req->frame_and_sects[0]) != 7) )
+ goto err;
+
+ /* loop for multiple gnbds would start here. */
+
+ gnbd_info = (vdisk_t *)MMAP_VADDR(ID_TO_IDX(req->id), 0);
+ gnbd_info[0].device = gnbd->vdevice;
+ gnbd_info[0].info = VDISK_TYPE_DISK | VDISK_FLAG_VIRT;
+ gnbd_info[0].capacity = gnbd_sectors(gnbd->gh);
+
+ printf("[SECTORS] %llu", gnbd_info[0].capacity);
+
+ //if (gnbd_info[0].capacity == 0)
+ // gnbd_info[0].capacity = ((u64)1 << 63); // xend does this too.
+
+ DPRINTF("iPROBE! device: 0x%04x capacity: %llu\n", gnbd_info[0].device,
+ gnbd_info[0].capacity);
+
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = BLKIF_OP_PROBE;
+ rsp->status = 1; /* number of disks */
+
+ return BLKTAP_RESPOND;
+err:
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+}
+
+int gnbd_request(blkif_request_t *req)
+{
+ struct gnbd_handle *gh;
+ u64 sector;
+ char *spage, *dpage;
+ int ret, i, idx;
+ blkif_response_t *rsp;
+ domid_t dom = ID_TO_DOM(req->id);
+
+ if ((gnbds[dom] == NULL) || (gnbds[dom]->gh == NULL)) {
+ printf("Data request for unknown domain!!! %d\n", dom);
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+ }
+
+ gh = gnbds[dom]->gh;
+
+ switch (req->operation)
+ {
+ case BLKIF_OP_PROBE:
+ {
+ printf("PROBE!\n");
+ if ( gnbds[dom]->gh_state == GH_PROBEWAITING ) {
+ printf("Already have a PROBE outstanding!\n");
+ goto err;
+ }
+
+ if ( gnbds[dom]->gh_state == GH_DISCONNECTED )
+ {
+ /* need to defer until we are connected. */
+ printf("Deferring PROBE!\n");
+ idx = ID_TO_IDX(req->id);
+ memcpy(&pending_list[idx].req, req, sizeof(*req));
+ ASSERT(pending_list[idx].count == 0);
+ pending_list[idx].count = 1;
+
+ gnbds[dom]->probe_idx = idx;
+ gnbds[dom]->gh_state = GH_PROBEWAITING;
+
+ return BLKTAP_STOLEN;
+ }
+
+
+ return gnbd_blkif_probe(req, gnbds[dom]);
+ }
+ case BLKIF_OP_WRITE:
+ {
+ unsigned long size;
+
+ idx = ID_TO_IDX(req->id);
+ ASSERT(pending_list[idx].count == 0);
+ memcpy(&pending_list[idx].req, req, sizeof(*req));
+ pending_list[idx].count = req->nr_segments;
+ pending_count++; /* dbg */
+
+ for (i = 0; i < req->nr_segments; i++) {
+
+ sector = req->sector_number + (8*i);
+
+ size = blkif_last_sect (req->frame_and_sects[i]) -
+ blkif_first_sect(req->frame_and_sects[i]) + 1;
+
+ DPRINTF("iWRITE: sec_nr: %10llu sec: %10llu (%1lu,%1lu) pos: %15lu\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << SECTOR_SHIFT));
+
+ spage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ spage += blkif_first_sect(req->frame_and_sects[i]) << SECTOR_SHIFT;
+
+ ret = gnbd_write(gh, sector, size, spage, (unsigned long)idx);
+ if (ret) {
+ printf("gnbd error on WRITE\n");
+ goto err;
+ }
+ }
+//printf("[WR] < %lu\n", (unsigned long)idx);
+
+ return BLKTAP_STOLEN;
+ }
+ case BLKIF_OP_READ:
+ {
+ unsigned long size;
+
+ idx = ID_TO_IDX(req->id);
+ ASSERT(pending_list[idx].count == 0);
+ memcpy(&pending_list[idx].req, req, sizeof(*req));
+ pending_list[idx].count = req->nr_segments;
+ pending_count++; /* dbg */
+
+ for (i = 0; i < req->nr_segments; i++) {
+
+ sector = req->sector_number + (8*i);
+
+ size = blkif_last_sect (req->frame_and_sects[i]) -
+ blkif_first_sect(req->frame_and_sects[i]) + 1;
+
+ DPRINTF("iREAD : sec_nr: %10llu sec: %10llu (%1lu,%1lu) pos: %15lu\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << SECTOR_SHIFT));
+
+ dpage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ dpage += blkif_first_sect(req->frame_and_sects[i]) << SECTOR_SHIFT;
+
+ ret = gnbd_read(gh, sector, size, dpage, (unsigned long)idx);
+ if (ret) {
+ printf("gnbd error on READ\n");
+ goto err;
+ }
+
+ }
+//printf("[RD] < %lu\n", (unsigned long)idx);
+
+ return BLKTAP_STOLEN;
+ }
+ }
+
+ printf("Unknown block operation!\n");
+err:
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+}
+
+/* the gnbd library terminates the request stream. _resp is a noop. */
+int gnbd_response(blkif_response_t *rsp)
+{
+ return BLKTAP_PASS;
+}
+
+int gnbd_pollhook(int fd)
+{
+ int err;
+ struct gnbd_handle *gh;
+ blkif_request_t *req;
+ blkif_response_t *rsp;
+ unsigned long idx;
+
+ gnbd_t *gnbd = get_gnbd_by_fd(fd);
+
+ if (gnbd == NULL) {
+ printf("GNBD badness: got poll hook on unknown device. (%d)\n", fd);
+ return -1;
+ }
+ gh = gnbd->gh;
+ err = gnbd_reply(gh);
+ switch (err) {
+ case GNBD_LOGIN_DONE:
+ if (gnbd->gh_state == GH_PROBEWAITING) {
+ req = (blkif_request_t *)&pending_list[gnbd->probe_idx].req;
+ printf("[!] Sending deferred PROBE!\n");
+ gnbd_blkif_probe(req, gnbd);
+ pending_list[gnbd->probe_idx].count = 0;
+ rsp = (blkif_response_t *)req;
+ blktap_inject_response(rsp);
+ }
+ gnbd->gh_state = GH_CONNECTED;
+ printf("GNBD_LOGIN_DONE (%d)\n", fd);
+ break;
+
+ case GNBD_REQUEST_DONE: /* switch to idx */
+ idx = gnbd_finished_request(gh);
+ req = (blkif_request_t *)&pending_list[idx].req;
+ if ((idx > MAX_REQUESTS-1) || (pending_list[idx].count == 0)){
+ printf("gnbd returned a bad cookie (%lu)!\n", idx);
+ break;
+ }
+
+ pending_list[idx].count--;
+
+ if (pending_list[idx].count == 0) {
+ blkif_request_t tmp = *req;
+ pending_count--; /* dbg */
+ rsp = (blkif_response_t *)req;
+ rsp->id = tmp.id;
+ rsp->operation = tmp.operation;
+ rsp->status = BLKIF_RSP_OKAY;
+ blktap_inject_response(rsp);
+/*
+if (rsp->operation == BLKIF_OP_READ) {
+printf("[RD] > %lu (%d pndg)\n", (unsigned long)idx, pending_count);
+} else if (rsp->operation == BLKIF_OP_WRITE) {
+printf("[WR] > %lu (%d pndg)\n", (unsigned long)idx, pending_count);
+} else {
+printf("[??] > %lu (%d pndg)\n", (unsigned long)idx, pending_count);
+}
+*/
+ }
+ break;
+
+ case GNBD_CONTINUE:
+ break;
+
+ case 0:
+ break;
+
+ default:
+ printf("gnbd_reply error");
+ break;
+ }
+ return 0;
+}
+
+void gnbd_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_DOMS; i++)
+ gnbds[i] = NULL;
+
+ for (i = 0; i < MAX_REQUESTS; i++)
+ pending_list[i].count = 0;
+
+ printf("GNBD image plugin initialized\n");
+}
+
diff --git a/tools/blktap/blkgnbdlib.h b/tools/blktap/blkgnbdlib.h
new file mode 100644
index 0000000000..b95d2409ac
--- /dev/null
+++ b/tools/blktap/blkgnbdlib.h
@@ -0,0 +1,16 @@
+/* blkgnbdlib.h
+ *
+ * gndb image-backed block device.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent.
+ */
+
+int gnbd_control(control_msg_t *msg);
+int gnbd_request(blkif_request_t *req);
+int gnbd_response(blkif_response_t *rsp); /* noop */
+void gnbd_init(void);
diff --git a/tools/blktap/blkimg.c b/tools/blktap/blkimg.c
new file mode 100644
index 0000000000..fc746add4b
--- /dev/null
+++ b/tools/blktap/blkimg.c
@@ -0,0 +1,19 @@
+/* blkimg.c
+ *
+ * file-backed disk.
+ */
+
+#include "blktaplib.h"
+#include "blkimglib.h"
+
+
+int main(int argc, char *argv[])
+{
+ image_init();
+
+ blktap_register_ctrl_hook("image_control", image_control);
+ blktap_register_request_hook("image_request", image_request);
+ blktap_listen();
+
+ return 0;
+}
diff --git a/tools/blktap/blkimglib.c b/tools/blktap/blkimglib.c
new file mode 100644
index 0000000000..075a2d962d
--- /dev/null
+++ b/tools/blktap/blkimglib.c
@@ -0,0 +1,325 @@
+/* blkimglib.c
+ *
+ * file image-backed block device.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <db.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <errno.h>
+#include "blktaplib.h"
+
+//#define TMP_IMAGE_FILE_NAME "/dev/sda1"
+#define TMP_IMAGE_FILE_NAME "fc3.image"
+
+#define MAX_DOMS 1024
+#define MAX_IMGNAME_LEN 255
+#define AMORFS_DEV 61440
+#define MAX_REQUESTS 64 /* must be synced with the blkif drivers. */
+#define SECTOR_SHIFT 9
+
+#if 0
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+
+typedef struct {
+ /* These need to turn into an array/rbtree for multi-disk support. */
+ FILE *img;
+ u64 fsid;
+ char imgname[MAX_IMGNAME_LEN];
+ blkif_vdev_t vdevice;
+} image_t;
+
+image_t *images[MAX_DOMS];
+blkif_request_t *reread_list[MAX_REQUESTS];
+
+int image_control(control_msg_t *msg)
+{
+ domid_t domid;
+ DB *db;
+ int ret;
+
+ if (msg->type != CMSG_BLKIF_BE)
+ {
+ printf("***\nUNEXPECTED CTRL MSG MAJOR TYPE(%d)\n***\n", msg->type);
+ return 0;
+ }
+
+ switch(msg->subtype)
+ {
+ case CMSG_BLKIF_BE_CREATE:
+ if ( msg->length != sizeof(blkif_be_create_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_CREATE(d:%d,h:%d)\n",
+ ((blkif_be_create_t *)msg->msg)->domid,
+ ((blkif_be_create_t *)msg->msg)->blkif_handle);
+ domid = ((blkif_be_create_t *)msg->msg)->domid;
+ if (images[domid] != NULL) {
+ printf("attempt to connect from an existing dom!\n");
+ return 0;
+ }
+
+ images[domid] = (image_t *)malloc(sizeof(image_t));
+ if (images[domid] == NULL) {
+ printf("error allocating image record.\n");
+ return 0;
+ }
+
+ images[domid]->img = NULL;
+ images[domid]->fsid = 0;
+
+ printf("Image connected.\n");
+ break;
+
+ case CMSG_BLKIF_BE_DESTROY:
+ if ( msg->length != sizeof(blkif_be_destroy_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_DESTROY(d:%d,h:%d)\n",
+ ((blkif_be_destroy_t *)msg->msg)->domid,
+ ((blkif_be_destroy_t *)msg->msg)->blkif_handle);
+
+ domid = ((blkif_be_destroy_t *)msg->msg)->domid;
+ if (images[domid] != NULL) {
+ if (images[domid]->img != NULL)
+ fclose( images[domid]->img );
+ free( images[domid] );
+ images[domid] = NULL;
+ }
+ break;
+ case CMSG_BLKIF_BE_VBD_GROW:
+ {
+ blkif_be_vbd_grow_t *grow;
+
+ if ( msg->length != sizeof(blkif_be_vbd_grow_t) )
+ goto parse_error;
+ printf("[CONTROL_MSG] CMSG_BLKIF_BE_VBD_GROW(d:%d,h:%d,v:%d)\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->domid,
+ ((blkif_be_vbd_grow_t *)msg->msg)->blkif_handle,
+ ((blkif_be_vbd_grow_t *)msg->msg)->vdevice);
+ printf(" Extent: sec_start: %llu sec_len: %llu, dev: %d\n",
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_start,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.sector_length,
+ ((blkif_be_vbd_grow_t *)msg->msg)->extent.device);
+ grow = (blkif_be_vbd_grow_t *)msg->msg;
+ domid = grow->domid;
+ if (images[domid] == NULL) {
+ printf("VBD_GROW on unconnected domain!\n");
+ return 0;
+ }
+
+ if (grow->extent.device != AMORFS_DEV) {
+ printf("VBD_GROW on non-amorfs device!\n");
+ return 0;
+ }
+
+ /* TODO: config support for arbitrary image files/modes. */
+ sprintf(images[domid]->imgname, TMP_IMAGE_FILE_NAME);
+
+ images[domid]->fsid = grow->extent.sector_start;
+ images[domid]->vdevice = grow->vdevice;
+ images[domid]->img = fopen64(TMP_IMAGE_FILE_NAME, "r+");
+ if (images[domid]->img == NULL) {
+ printf("Couldn't open image file!\n");
+ return 0;
+ }
+
+ printf("Image file opened. (%s)\n", images[domid]->imgname);
+ break;
+ }
+ }
+ return 0;
+parse_error:
+ printf("Bad control message!\n");
+ return 0;
+
+create_failed:
+ /* TODO: close the db ref. */
+ return 0;
+}
+
+int image_request(blkif_request_t *req)
+{
+ FILE *img;
+ u64 sector;
+ char *spage, *dpage;
+ int ret, i, idx;
+ blkif_response_t *rsp;
+ domid_t dom = ID_TO_DOM(req->id);
+
+ if ((images[dom] == NULL) || (images[dom]->img == NULL)) {
+ printf("Data request for unknown domain!!! %d\n", dom);
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+ }
+
+ img = images[dom]->img;
+
+ switch (req->operation)
+ {
+ case BLKIF_OP_PROBE:
+ {
+ int fd;
+ struct stat stat;
+ vdisk_t *img_info;
+
+
+ /* We expect one buffer only. */
+ if ( req->nr_segments != 1 )
+ goto err;
+
+ /* Make sure the buffer is page-sized. */
+ if ( (blkif_first_sect(req->frame_and_sects[0]) != 0) ||
+ (blkif_last_sect (req->frame_and_sects[0]) != 7) )
+ goto err;
+
+ /* loop for multiple images would start here. */
+
+ fd = fileno(img);
+ if (fd == -1) {
+ printf("Couldn't get image fd in PROBE!\n");
+ goto err;
+ }
+
+ ret = fstat(fd, &stat);
+ if (ret != 0) {
+ printf("Couldn't stat image in PROBE!\n");
+ goto err;
+ }
+
+ img_info = (vdisk_t *)MMAP_VADDR(ID_TO_IDX(req->id), 0);
+ img_info[0].device = images[dom]->vdevice;
+ img_info[0].info = VDISK_TYPE_DISK | VDISK_FLAG_VIRT;
+ img_info[0].capacity = (stat.st_size >> SECTOR_SHIFT);
+
+ if (img_info[0].capacity == 0)
+ img_info[0].capacity = ((u64)1 << 63); // xend does this too.
+
+ DPRINTF("iPROBE! device: 0x%04x capacity: %llu\n", img_info[0].device,
+ img_info[0].capacity);
+
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = BLKIF_OP_PROBE;
+ rsp->status = 1; /* number of disks */
+
+ return BLKTAP_RESPOND;
+ }
+ case BLKIF_OP_WRITE:
+ {
+ unsigned long size;
+
+ for (i = 0; i < req->nr_segments; i++) {
+
+ sector = req->sector_number + (8*i);
+
+ size = blkif_last_sect (req->frame_and_sects[i]) -
+ blkif_first_sect(req->frame_and_sects[i]) + 1;
+
+ ret = fseeko64(img, (off_t)(sector << SECTOR_SHIFT), SEEK_SET);
+ if (ret != 0) {
+ printf("fseek error on WRITE\n");
+ goto err;
+ }
+
+ DPRINTF("iWRITE: sec_nr: %10llu sec: %10llu (%1lu,%1lu) pos: %15lu\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << SECTOR_SHIFT));
+
+ spage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ spage += blkif_first_sect(req->frame_and_sects[i]) << SECTOR_SHIFT;
+ ret = fwrite(spage, size << SECTOR_SHIFT, 1, img);
+ if (ret != 1) {
+ printf("fwrite error on WRITE (%d)\n", errno);
+ goto err;
+ }
+ }
+
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = BLKIF_OP_WRITE;
+ rsp->status = BLKIF_RSP_OKAY;
+
+ return BLKTAP_RESPOND;
+ }
+ case BLKIF_OP_READ:
+ {
+ unsigned long size;
+
+ for (i = 0; i < req->nr_segments; i++) {
+
+ sector = req->sector_number + (8*i);
+
+ size = blkif_last_sect (req->frame_and_sects[i]) -
+ blkif_first_sect(req->frame_and_sects[i]) + 1;
+
+ ret = fseeko64(img, (off_t)(sector << SECTOR_SHIFT), SEEK_SET);
+ if (ret != 0) {
+ printf("fseek error on READ\n");
+ goto err;
+ }
+
+ DPRINTF("iREAD : sec_nr: %10llu sec: %10llu (%1lu,%1lu) pos: %15lu\n",
+ req->sector_number, sector,
+ blkif_first_sect(req->frame_and_sects[i]),
+ blkif_last_sect (req->frame_and_sects[i]),
+ (long)(sector << SECTOR_SHIFT));
+
+ dpage = (char *)MMAP_VADDR(ID_TO_IDX(req->id), i);
+ dpage += blkif_first_sect(req->frame_and_sects[i]) << SECTOR_SHIFT;
+ ret = fread(dpage, size << SECTOR_SHIFT, 1, img);
+ if (ret != 1) {
+ printf("fread error on READ\n");
+ goto err;
+ }
+ }
+
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = BLKIF_OP_READ;
+ rsp->status = BLKIF_RSP_OKAY;
+ return BLKTAP_RESPOND;
+ }
+ }
+
+ printf("Unknow block operation!\n");
+err:
+ rsp = (blkif_response_t *)req;
+ rsp->id = req->id;
+ rsp->operation = req->operation;
+ rsp->status = BLKIF_RSP_ERROR;
+ return BLKTAP_RESPOND;
+}
+
+/* the image library terminates the request stream. _resp is a noop. */
+int image_response(blkif_response_t *rsp)
+{
+ return BLKTAP_PASS;
+}
+
+void image_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_DOMS; i++)
+ images[i] = NULL;
+}
+
diff --git a/tools/blktap/blkimglib.h b/tools/blktap/blkimglib.h
new file mode 100644
index 0000000000..1bc597f233
--- /dev/null
+++ b/tools/blktap/blkimglib.h
@@ -0,0 +1,16 @@
+/* blkimglib.h
+ *
+ * file image-backed block device.
+ *
+ * (c) 2004 Andrew Warfield.
+ *
+ * Xend has been modified to use an amorfs:[fsid] disk tag.
+ * This will show up as device type (maj:240,min:0) = 61440.
+ *
+ * The fsid is placed in the sec_start field of the disk extent.
+ */
+
+int image_control(control_msg_t *msg);
+int image_request(blkif_request_t *req);
+int image_response(blkif_response_t *rsp); /* noop */
+void image_init(void);
diff --git a/tools/blktap/blkint.h b/tools/blktap/blkint.h
new file mode 100644
index 0000000000..e3ce3b55e1
--- /dev/null
+++ b/tools/blktap/blkint.h
@@ -0,0 +1,105 @@
+/*
+ * blkint.h
+ *
+ * Interfaces for the Xen block interposition driver.
+ *
+ * (c) 2004, Andrew Warfield, University of Cambridge
+ *
+ */
+
+#ifndef __BLKINT_H__
+
+//#include "blkif.h"
+
+
+#if 0
+/* Types of ring. */
+#define BLKIF_REQ_RING_TYPE 1
+#define BLKIF_RSP_RING_TYPE 2
+
+/* generic ring struct. */
+typedef struct blkif_generic_ring_struct {
+ int type;
+} blkif_generic_ring_t;
+
+/* A requestor's view of a ring. */
+typedef struct blkif_req_ring_struct {
+
+ int type; /* Will be BLKIF_REQ_RING_TYPE */
+ BLKIF_RING_IDX req_prod; /* PRIVATE req_prod index */
+ BLKIF_RING_IDX rsp_cons; /* Response consumer index */
+ blkif_ring_t *ring; /* Pointer to shared ring struct */
+
+} blkif_req_ring_t;
+
+#define BLKIF_REQ_RING_INIT { BLKIF_REQ_RING_TYPE, 0, 0, 0 }
+
+/* A responder's view of a ring. */
+typedef struct blkif_rsp_ring_struct {
+
+ int type; /* Will be BLKIF_REQ_RING_TYPE */
+ BLKIF_RING_IDX rsp_prod; /* PRIVATE rsp_prod index */
+ BLKIF_RING_IDX req_cons; /* Request consumer index */
+ blkif_ring_t *ring; /* Pointer to shared ring struct */
+
+} blkif_rsp_ring_t;
+
+#define BLKIF_RSP_RING_INIT { BLKIF_RSP_RING_TYPE, 0, 0, 0 }
+
+#define RING(a) (blkif_generic_ring_t *)(a)
+inline int BLKTAP_RING_FULL(blkif_generic_ring_t *ring);
+#endif
+
+/* -------[ interposition -> character device interface ]------------- */
+
+/* /dev/xen/blktap resides at device number major=10, minor=202 */
+#define BLKTAP_MINOR 202
+
+/* size of the extra VMA area to map in attached pages. */
+#define BLKTAP_VMA_PAGES BLKIF_RING_SIZE
+
+/* blktap IOCTLs: */
+#define BLKTAP_IOCTL_KICK_FE 1
+#define BLKTAP_IOCTL_KICK_BE 2
+#define BLKTAP_IOCTL_SETMODE 3
+#define BLKTAP_IOCTL_PRINT_IDXS 100
+
+/* blktap switching modes: (Set with BLKTAP_IOCTL_SETMODE) */
+#define BLKTAP_MODE_PASSTHROUGH 0x00000000 /* default */
+#define BLKTAP_MODE_INTERCEPT_FE 0x00000001
+#define BLKTAP_MODE_INTERCEPT_BE 0x00000002
+#define BLKTAP_MODE_COPY_FE 0x00000004
+#define BLKTAP_MODE_COPY_BE 0x00000008
+#define BLKTAP_MODE_COPY_FE_PAGES 0x00000010
+#define BLKTAP_MODE_COPY_BE_PAGES 0x00000020
+
+#define BLKTAP_MODE_INTERPOSE \
+ (BLKTAP_MODE_INTERCEPT_FE | BLKTAP_MODE_INTERCEPT_BE)
+
+#define BLKTAP_MODE_COPY_BOTH \
+ (BLKTAP_MODE_COPY_FE | BLKTAP_MODE_COPY_BE)
+
+#define BLKTAP_MODE_COPY_BOTH_PAGES \
+ (BLKTAP_MODE_COPY_FE_PAGES | BLKTAP_MODE_COPY_BE_PAGES)
+
+static inline int BLKTAP_MODE_VALID(unsigned long arg)
+{
+ return (
+ ( arg == BLKTAP_MODE_PASSTHROUGH ) ||
+ ( arg == BLKTAP_MODE_INTERCEPT_FE ) ||
+ ( arg == BLKTAP_MODE_INTERCEPT_BE ) ||
+ ( arg == BLKTAP_MODE_INTERPOSE ) ||
+ ( (arg & ~BLKTAP_MODE_COPY_FE_PAGES) == BLKTAP_MODE_COPY_FE ) ||
+ ( (arg & ~BLKTAP_MODE_COPY_BE_PAGES) == BLKTAP_MODE_COPY_BE ) ||
+ ( (arg & ~BLKTAP_MODE_COPY_BOTH_PAGES) == BLKTAP_MODE_COPY_BOTH )
+ );
+}
+
+
+
+
+
+
+
+#define __BLKINT_H__
+#endif
diff --git a/tools/blktap/blktaplib.c b/tools/blktap/blktaplib.c
new file mode 100644
index 0000000000..2399a20d7a
--- /dev/null
+++ b/tools/blktap/blktaplib.c
@@ -0,0 +1,542 @@
+/*
+ * blktaplib.c
+ *
+ * userspace interface routines for the blktap driver.
+ *
+ * (c) 2004 Andrew Warfield.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+#include <err.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <string.h>
+#include <unistd.h>
+
+
+#define __COMPILING_BLKTAP_LIB
+#include "blktaplib.h"
+
+#if 1
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+#define DEBUG_RING_IDXS 1
+
+#define POLLRDNORM 0x040
+
+#define BLKTAP_IOCTL_KICK 1
+
+// this is in the header now
+//DEFINE_RING_TYPES(blkif, blkif_request_t, blkif_response_t);
+
+void got_sig_bus();
+void got_sig_int();
+
+
+/* in kernel these are opposite, but we are a consumer now. */
+blkif_back_ring_t fe_ring; /* slightly counterintuitive ;) */
+blkif_front_ring_t be_ring;
+ctrl_back_ring_t ctrl_ring;
+
+
+
+unsigned long mmap_vstart = 0;
+char *blktap_mem;
+int fd = 0;
+
+#define BLKTAP_RING_PAGES 3 /* Ctrl, Back, Front */
+/*#define BLKTAP_MMAP_PAGES ((11 + 1) * 64)*/
+#define BLKTAP_MMAP_PAGES \
+ ((BLKIF_MAX_SEGMENTS_PER_REQUEST + 1) * BLKIF_RING_SIZE)
+#define BLKTAP_MMAP_REGION_SIZE (BLKTAP_RING_PAGES + BLKTAP_MMAP_PAGES)
+
+
+
+int bad_count = 0;
+void bad(void)
+{
+ bad_count ++;
+ if (bad_count > 50) exit(0);
+}
+/*-----[ ID Manipulation from tap driver code ]--------------------------*/
+
+#define ACTIVE_RING_IDX unsigned short
+
+inline unsigned long MAKE_ID(domid_t fe_dom, ACTIVE_RING_IDX idx)
+{
+ return ( (fe_dom << 16) | idx );
+}
+
+inline unsigned int ID_TO_IDX(unsigned long id)
+{
+ return ( id & 0x0000ffff );
+}
+
+inline domid_t ID_TO_DOM(unsigned long id) { return (id >> 16); }
+/*
+static int (*request_hook)(blkif_request_t *req) = NULL;
+static int (*response_hook)(blkif_response_t *req) = NULL;
+*/
+
+/*-----[ Request/Response hook chains.]----------------------------------*/
+
+#define HOOK_NAME_MAX 50
+
+typedef struct ctrl_hook_st {
+ char name[HOOK_NAME_MAX];
+ int (*func)(control_msg_t *);
+ struct ctrl_hook_st *next;
+} ctrl_hook_t;
+
+typedef struct request_hook_st {
+ char name[HOOK_NAME_MAX];
+ int (*func)(blkif_request_t *);
+ struct request_hook_st *next;
+} request_hook_t;
+
+typedef struct response_hook_st {
+ char name[HOOK_NAME_MAX];
+ int (*func)(blkif_response_t *);
+ struct response_hook_st *next;
+} response_hook_t;
+
+static ctrl_hook_t *ctrl_hook_chain = NULL;
+static request_hook_t *request_hook_chain = NULL;
+static response_hook_t *response_hook_chain = NULL;
+
+void blktap_register_ctrl_hook(char *name, int (*ch)(control_msg_t *))
+{
+ ctrl_hook_t *ch_ent, **c;
+
+ ch_ent = (ctrl_hook_t *)malloc(sizeof(ctrl_hook_t));
+ if (!ch_ent) { printf("couldn't allocate a new hook\n"); exit(-1); }
+
+ ch_ent->func = ch;
+ ch_ent->next = NULL;
+ strncpy(ch_ent->name, name, HOOK_NAME_MAX);
+ ch_ent->name[HOOK_NAME_MAX-1] = '\0';
+
+ c = &ctrl_hook_chain;
+ while (*c != NULL) {
+ c = &(*c)->next;
+ }
+ *c = ch_ent;
+}
+
+void blktap_register_request_hook(char *name, int (*rh)(blkif_request_t *))
+{
+ request_hook_t *rh_ent, **c;
+
+ rh_ent = (request_hook_t *)malloc(sizeof(request_hook_t));
+ if (!rh_ent) { printf("couldn't allocate a new hook\n"); exit(-1); }
+
+ rh_ent->func = rh;
+ rh_ent->next = NULL;
+ strncpy(rh_ent->name, name, HOOK_NAME_MAX);
+
+ c = &request_hook_chain;
+ while (*c != NULL) {
+ c = &(*c)->next;
+ }
+ *c = rh_ent;
+}
+
+void blktap_register_response_hook(char *name, int (*rh)(blkif_response_t *))
+{
+ response_hook_t *rh_ent, **c;
+
+ rh_ent = (response_hook_t *)malloc(sizeof(response_hook_t));
+ if (!rh_ent) { printf("couldn't allocate a new hook\n"); exit(-1); }
+
+ rh_ent->func = rh;
+ rh_ent->next = NULL;
+ strncpy(rh_ent->name, name, HOOK_NAME_MAX);
+
+ c = &response_hook_chain;
+ while (*c != NULL) {
+ c = &(*c)->next;
+ }
+ *c = rh_ent;
+}
+
+void print_hooks(void)
+{
+ request_hook_t *req_hook;
+ response_hook_t *rsp_hook;
+ ctrl_hook_t *ctrl_hook;
+
+ printf("Control Hooks:\n");
+ ctrl_hook = ctrl_hook_chain;
+ while (ctrl_hook != NULL)
+ {
+ printf(" [0x%p] %s\n", ctrl_hook->func, ctrl_hook->name);
+ ctrl_hook = ctrl_hook->next;
+ }
+
+ printf("Request Hooks:\n");
+ req_hook = request_hook_chain;
+ while (req_hook != NULL)
+ {
+ printf(" [0x%p] %s\n", req_hook->func, req_hook->name);
+ req_hook = req_hook->next;
+ }
+
+ printf("Response Hooks:\n");
+ rsp_hook = response_hook_chain;
+ while (rsp_hook != NULL)
+ {
+ printf(" [0x%p] %s\n", rsp_hook->func, rsp_hook->name);
+ rsp_hook = rsp_hook->next;
+ }
+}
+
+/*-----[ Data to/from Backend (server) VM ]------------------------------*/
+
+inline int write_req_to_be_ring(blkif_request_t *req)
+{
+ blkif_request_t *req_d;
+
+ //req_d = FRONT_RING_NEXT_EMPTY_REQUEST(&be_ring);
+ req_d = RING_GET_REQUEST(BLKIF_RING, &be_ring, be_ring.req_prod_pvt);
+ memcpy(req_d, req, sizeof(blkif_request_t));
+ wmb();
+ be_ring.req_prod_pvt++;
+
+ return 0;
+}
+
+inline int write_rsp_to_fe_ring(blkif_response_t *rsp)
+{
+ blkif_response_t *rsp_d;
+
+ //rsp_d = BACK_RING_NEXT_EMPTY_RESPONSE(&fe_ring);
+ rsp_d = RING_GET_RESPONSE(BLKIF_RING, &fe_ring, fe_ring.rsp_prod_pvt);
+ memcpy(rsp_d, rsp, sizeof(blkif_response_t));
+ wmb();
+ fe_ring.rsp_prod_pvt++;
+
+ return 0;
+}
+
+static void apply_rsp_hooks(blkif_response_t *rsp)
+{
+ response_hook_t *rsp_hook;
+
+ rsp_hook = response_hook_chain;
+ while (rsp_hook != NULL)
+ {
+ switch(rsp_hook->func(rsp))
+ {
+ case BLKTAP_PASS:
+ break;
+ default:
+ printf("Only PASS is supported for resp hooks!\n");
+ }
+ rsp_hook = rsp_hook->next;
+ }
+}
+
+void blktap_inject_response(blkif_response_t *rsp)
+{
+ apply_rsp_hooks(rsp);
+ write_rsp_to_fe_ring(rsp);
+ RING_PUSH_RESPONSES(BLKIF_RING, &fe_ring);
+ ioctl(fd, BLKTAP_IOCTL_KICK_FE);
+}
+
+/*-----[ Polling fd listeners ]------------------------------------------*/
+
+#define MAX_POLLFDS 64
+
+typedef struct {
+ int (*func)(int fd);
+ struct pollfd *pfd;
+ int fd;
+ short events;
+ int active;
+} pollhook_t;
+
+static struct pollfd pfd[MAX_POLLFDS+1];
+static pollhook_t pollhooks[MAX_POLLFDS];
+static unsigned int ph_freelist[MAX_POLLFDS];
+static unsigned int ph_cons, ph_prod;
+#define nr_pollhooks() (MAX_POLLFDS - (ph_prod - ph_cons))
+#define PH_IDX(x) (x % MAX_POLLFDS)
+
+int blktap_attach_poll(int fd, short events, int (*func)(int fd))
+{
+ pollhook_t *ph;
+
+ if (nr_pollhooks() == MAX_POLLFDS) {
+ printf("Too many pollhooks!\n");
+ return -1;
+ }
+
+ ph = &pollhooks[ph_freelist[PH_IDX(ph_cons++)]];
+
+ ph->func = func;
+ ph->fd = fd;
+ ph->events = events;
+ ph->active = 1;
+
+ printf("Added fd %d at ph index %d, now %d phs.\n", fd, ph_cons-1,
+ nr_pollhooks());
+
+ return 0;
+}
+
+void blktap_detach_poll(int fd)
+{
+ int i;
+
+ for (i=0; i<MAX_POLLFDS; i++)
+ if ((pollhooks[i].active) && (pollhooks[i].pfd->fd == fd)) {
+ ph_freelist[PH_IDX(ph_prod++)] = i;
+ pollhooks[i].pfd->fd = -1;
+ pollhooks[i].active = 0;
+ break;
+ }
+
+ printf("Removed fd %d at ph index %d, now %d phs.\n", fd, i,
+ nr_pollhooks());
+}
+
+void pollhook_init(void)
+{
+ int i;
+
+ for (i=0; i < MAX_POLLFDS; i++) {
+ ph_freelist[i] = (i+1) % MAX_POLLFDS;
+ pollhooks[i].active = 0;
+ }
+
+ ph_cons = 0;
+ ph_prod = MAX_POLLFDS;
+}
+
+void __attribute__ ((constructor)) blktaplib_init(void)
+{
+ printf("[[ C O N S T R U C T O R ]]\n");
+ pollhook_init();
+}
+
+/*-----[ The main listen loop ]------------------------------------------*/
+
+int blktap_listen(void)
+{
+ int notify_be, notify_fe, tap_pfd;
+
+ /* comms rings: */
+ blkif_request_t *req;
+ blkif_response_t *rsp;
+ control_msg_t *msg;
+ blkif_sring_t *sring;
+ ctrl_sring_t *csring;
+ RING_IDX rp, i, pfd_count;
+
+ /* handler hooks: */
+ request_hook_t *req_hook;
+ response_hook_t *rsp_hook;
+ ctrl_hook_t *ctrl_hook;
+
+ signal (SIGBUS, got_sig_bus);
+ signal (SIGINT, got_sig_int);
+
+ print_hooks();
+
+ fd = open("/dev/blktap", O_RDWR);
+ if (fd == -1) {
+ printf("open failed! (%d)\n", errno);
+ goto open_failed;
+ }
+
+ blktap_mem = mmap(0, PAGE_SIZE * BLKTAP_MMAP_REGION_SIZE,
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+ if ((int)blktap_mem == -1) {
+ printf("mmap failed! (%d)\n", errno);
+ goto mmap_failed;
+ }
+
+ /* assign the rings to the mapped memory */
+ csring = (ctrl_sring_t *)blktap_mem;
+ BACK_RING_INIT(CTRL_RING, &ctrl_ring, csring);
+
+ sring = (blkif_sring_t *)((unsigned long)blktap_mem + PAGE_SIZE);
+ FRONT_RING_INIT(BLKIF_RING, &be_ring, sring);
+
+ sring = (blkif_sring_t *)((unsigned long)blktap_mem + (2 *PAGE_SIZE));
+ BACK_RING_INIT(BLKIF_RING, &fe_ring, sring);
+
+ mmap_vstart = (unsigned long)blktap_mem + (BLKTAP_RING_PAGES << PAGE_SHIFT);
+
+ printf("fe_ring mapped at: %p\n", fe_ring.sring);
+ printf("be_ring mapped at: %p\n", be_ring.sring);
+
+ ioctl(fd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_INTERPOSE );
+
+ while(1) {
+ int ret;
+
+ /* build the poll list */
+
+ DPRINTF("Building poll list.\n");
+
+ pfd_count = 0;
+ for ( i=0; i < MAX_POLLFDS; i++ ) {
+ pollhook_t *ph = &pollhooks[i];
+
+ if (ph->active) {
+ pfd[pfd_count].fd = ph->fd;
+ pfd[pfd_count].events = ph->events;
+ ph->pfd = &pfd[pfd_count];
+ pfd_count++;
+ }
+ }
+
+ tap_pfd = pfd_count;
+ pfd[tap_pfd].fd = fd;
+ pfd[tap_pfd].events = POLLIN;
+
+ DPRINTF("poll() %d fds.\n", pfd_count);
+
+ if ( (ret = (poll(pfd, pfd_count+1, 10000)) == 0) ) {
+ if (DEBUG_RING_IDXS)
+ ioctl(fd, BLKTAP_IOCTL_PRINT_IDXS);
+ continue;
+ }
+
+ DPRINTF("poll returned %d\n", ret);
+
+ for (i=0; i < MAX_POLLFDS; i++) {
+ if ( (pollhooks[i].active ) && (pollhooks[i].pfd->revents ) )
+ pollhooks[i].func(pollhooks[i].pfd->fd);
+ }
+
+ if (pfd[tap_pfd].revents) {
+
+ /* empty the control ring */
+ rp = ctrl_ring.sring->req_prod;
+ rmb();
+ for (i = ctrl_ring.req_cons; i < rp; i++)
+ {
+ msg = RING_GET_REQUEST(CTRL_RING, &ctrl_ring, i);
+
+ ctrl_hook = ctrl_hook_chain;
+ while (ctrl_hook != NULL)
+ {
+ DPRINTF("CTRL_HOOK: %s\n", ctrl_hook->name);
+ /* We currently don't respond to ctrl messages. */
+ ctrl_hook->func(msg);
+ ctrl_hook = ctrl_hook->next;
+ }
+ }
+ /* Using this as a unidirectional ring. */
+ ctrl_ring.req_cons = ctrl_ring.rsp_prod_pvt = i;
+ RING_PUSH_RESPONSES(CTRL_RING, &ctrl_ring);
+
+ /* empty the fe_ring */
+ notify_fe = 0;
+ notify_be = RING_HAS_UNCONSUMED_REQUESTS(BLKIF_RING, &fe_ring);
+ rp = fe_ring.sring->req_prod;
+ rmb();
+ for (i = fe_ring.req_cons; i != rp; i++)
+ {
+ int done = 0; /* stop forwarding this request */
+
+ req = RING_GET_REQUEST(BLKIF_RING, &fe_ring, i);
+
+ DPRINTF("copying an fe request\n");
+
+ req_hook = request_hook_chain;
+ while (req_hook != NULL)
+ {
+ DPRINTF("REQ_HOOK: %s\n", req_hook->name);
+ switch(req_hook->func(req))
+ {
+ case BLKTAP_RESPOND:
+ apply_rsp_hooks((blkif_response_t *)req);
+ write_rsp_to_fe_ring((blkif_response_t *)req);
+ notify_fe = 1;
+ done = 1;
+ break;
+ case BLKTAP_STOLEN:
+ done = 1;
+ break;
+ case BLKTAP_PASS:
+ break;
+ default:
+ printf("Unknown request hook return value!\n");
+ }
+ if (done) break;
+ req_hook = req_hook->next;
+ }
+
+ if (done == 0) write_req_to_be_ring(req);
+
+ }
+ fe_ring.req_cons = i;
+
+ /* empty the be_ring */
+ notify_fe |= RING_HAS_UNCONSUMED_RESPONSES(BLKIF_RING, &be_ring);
+ rp = be_ring.sring->rsp_prod;
+ rmb();
+ for (i = be_ring.rsp_cons; i != rp; i++)
+ {
+
+ rsp = RING_GET_RESPONSE(BLKIF_RING, &be_ring, i);
+
+ DPRINTF("copying a be request\n");
+
+ apply_rsp_hooks(rsp);
+ write_rsp_to_fe_ring(rsp);
+ }
+ be_ring.rsp_cons = i;
+
+ /* notify the domains */
+
+ if (notify_be) {
+ DPRINTF("notifying be\n");
+ RING_PUSH_REQUESTS(BLKIF_RING, &be_ring);
+ ioctl(fd, BLKTAP_IOCTL_KICK_BE);
+ }
+
+ if (notify_fe) {
+ DPRINTF("notifying fe\n");
+ RING_PUSH_RESPONSES(BLKIF_RING, &fe_ring);
+ ioctl(fd, BLKTAP_IOCTL_KICK_FE);
+ }
+ }
+ }
+
+
+ munmap(blktap_mem, PAGE_SIZE);
+
+ mmap_failed:
+ close(fd);
+
+ open_failed:
+ return 0;
+}
+
+void got_sig_bus() {
+ printf("Attempted to access a page that isn't.\n");
+ exit(-1);
+}
+
+void got_sig_int() {
+ printf("quitting -- returning to passthrough mode.\n");
+ if (fd > 0) ioctl(fd, BLKTAP_IOCTL_SETMODE, BLKTAP_MODE_PASSTHROUGH );
+ exit(0);
+}
diff --git a/tools/blktap/blktaplib.h b/tools/blktap/blktaplib.h
new file mode 100644
index 0000000000..7b38f565fc
--- /dev/null
+++ b/tools/blktap/blktaplib.h
@@ -0,0 +1,76 @@
+/* blktaplib.h
+ *
+ * userland accessors to the block tap.
+ *
+ * for the moment this is rather simple.
+ */
+
+#ifndef __BLKTAPLIB_H__
+#define __BLKTAPLIB_H__
+
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+#if defined(__i386__)
+#define rmb() __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define wmb() __asm__ __volatile__ ( "" : : : "memory" )
+#else
+#error "Define barriers"
+#endif
+
+#include <sys/user.h>
+#include <xen/xen.h>
+#include <xen/io/blkif.h>
+#include <xen/io/ring.h>
+#include <xen/io/domain_controller.h>
+#include "blkint.h"
+
+#define BLKTAP_PASS 0 /* Keep passing this request as normal. */
+#define BLKTAP_RESPOND 1 /* Request is now a reply. Return it. */
+#define BLKTAP_STOLEN 2 /* Hook has stolen request. */
+
+#define domid_t unsigned short
+
+inline unsigned int ID_TO_IDX(unsigned long id);
+inline domid_t ID_TO_DOM(unsigned long id);
+
+void blktap_register_ctrl_hook(char *name, int (*ch)(control_msg_t *));
+void blktap_register_request_hook(char *name, int (*rh)(blkif_request_t *));
+void blktap_register_response_hook(char *name, int (*rh)(blkif_response_t *));
+void blktap_inject_response(blkif_response_t *);
+int blktap_attach_poll(int fd, short events, int (*func)(int));
+void blktap_detach_poll(int fd);
+int blktap_listen(void);
+
+/*-----[ Accessing attached data page mappings ]-------------------------*/
+#define MMAP_PAGES_PER_REQUEST \
+ (BLKIF_MAX_SEGMENTS_PER_REQUEST + 1)
+#define MMAP_VADDR(_req,_seg) \
+ (mmap_vstart + \
+ ((_req) * MMAP_PAGES_PER_REQUEST * PAGE_SIZE) + \
+ ((_seg) * PAGE_SIZE))
+
+extern unsigned long mmap_vstart;
+
+
+/*-----[ Defines that are only used by library clients ]-----------------*/
+
+#ifndef __COMPILING_BLKTAP_LIB
+
+static char *blkif_op_name[] = {
+ [BLKIF_OP_READ] = "READ",
+ [BLKIF_OP_WRITE] = "WRITE",
+ [BLKIF_OP_PROBE] = "PROBE",
+};
+
+#endif /* __COMPILING_BLKTAP_LIB */
+
+#endif /* __BLKTAPLIB_H__ */
diff --git a/tools/blktap/libgnbd/Makefile b/tools/blktap/libgnbd/Makefile
new file mode 100644
index 0000000000..4297c02148
--- /dev/null
+++ b/tools/blktap/libgnbd/Makefile
@@ -0,0 +1,8 @@
+
+CFLAGS += -Wall -Werror -g
+LDFLAGS += -g
+
+libgnbd.a: libgnbd.o
+ $(AR) r $@ $<
+
+gnbdtest: gnbdtest.o libgnbd.a
diff --git a/tools/blktap/libgnbd/gnbdtest.c b/tools/blktap/libgnbd/gnbdtest.c
new file mode 100644
index 0000000000..bc391591b0
--- /dev/null
+++ b/tools/blktap/libgnbd/gnbdtest.c
@@ -0,0 +1,90 @@
+
+#include <err.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/poll.h>
+
+#include "libgnbd.h"
+
+#define PRINTF(x) printf x
+#if 0
+#define DFPRINTF(x...) fprintf(stderr, ##x)
+#define DPRINTF(x) DFPRINTF x
+#else
+#define DPRINTF(x)
+#endif
+
+static unsigned char buf1[8 << 9];
+static unsigned char buf2[8 << 9];
+static unsigned char buf3[8 << 9];
+
+int
+main(int argc, char **argv)
+{
+ struct gnbd_handle *gh;
+ struct pollfd pfd[1];
+ int err, tout;
+
+ gh = gnbd_setup("panik", 0x38e7, "cl349-nahant-beta2-root1",
+ "arcadians.cl.cam.ac.uk");
+ if (gh == NULL)
+ errx(1, "gnbd_setup");
+
+ memset(pfd, 0, sizeof(pfd));
+ pfd[0].fd = gnbd_fd(gh);
+ pfd[0].events = POLLIN;
+
+ while ((tout = poll(pfd, 1, 0)) >= 0) {
+ if (tout == 0)
+ continue;
+ DPRINTF(("event\n"));
+ if (pfd[0].revents) {
+ err = gnbd_reply(gh);
+ pfd[0].events = POLLIN;
+ switch (err) {
+ case GNBD_LOGIN_DONE:
+ DPRINTF(("sectors: %08llu\n",
+ gnbd_sectors(gh)));
+ err = gnbd_read(gh, 8, 8, buf2, 1);
+ if (err)
+ warnx("gnbd_read");
+ err = gnbd_read(gh, 0, 8, buf1, 0);
+ if (err)
+ warnx("gnbd_read");
+ err = gnbd_read(gh, 16, 8, buf3, 2);
+ if (err)
+ warnx("gnbd_read");
+ break;
+ case GNBD_REQUEST_DONE:
+ DPRINTF(("request done %ld\n",
+ gnbd_finished_request(gh)));
+ if (0 && gnbd_finished_request(gh) == 0) {
+ write(1, buf1, 8 << 9);
+ err = gnbd_write(gh, 0, 8, buf1, 10);
+ if (err)
+ warnx("gnbd_write");
+ }
+ break;
+ case GNBD_CONTINUE:
+ DPRINTF(("continue\n"));
+ break;
+ case 0:
+ break;
+ case GNBD_CONTINUE_WRITE:
+ DPRINTF(("continue write\n"));
+ pfd[0].events |= POLLOUT;
+ break;
+ default:
+ warnx("gnbd_reply error");
+ break;
+ }
+ DPRINTF(("got gnbd reply\n"));
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/blktap/libgnbd/libgnbd.c b/tools/blktap/libgnbd/libgnbd.c
new file mode 100644
index 0000000000..2856ca311d
--- /dev/null
+++ b/tools/blktap/libgnbd/libgnbd.c
@@ -0,0 +1,647 @@
+/* libgnbd.c
+ *
+ * gnbd client library
+ *
+ * Copyright (c) 2005, Christian Limpach
+ */
+
+#include <byteswap.h>
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <stdio.h>
+
+#include "libgnbd.h"
+
+#define PROTOCOL_VERSION 2
+
+#define EXTERN_KILL_GSERV_REQ 5
+#define EXTERN_LOGIN_REQ 6
+
+#define GNBD_REQUEST_MAGIC 0x37a07e00
+#define GNBD_KEEP_ALIVE_MAGIC 0x5b46d8c2
+#define GNBD_REPLY_MAGIC 0x41f09370
+
+enum {
+ GNBD_CMD_READ = 0,
+ GNBD_CMD_WRITE = 1,
+ GNBD_CMD_DISC = 2,
+ GNBD_CMD_PING = 3
+};
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define htonll(x) (x)
+#define ntohll(x) (x)
+#endif
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htonll(x) bswap_64(x)
+#define ntohll(x) bswap_64(x)
+#endif
+
+#define PRINTF(x) printf x
+#if 0
+#define DFPRINTF(x...) fprintf(stderr, ##x)
+#define DPRINTF(x) DFPRINTF x
+#else
+#define DPRINTF(x)
+#endif
+
+struct gnbd_request {
+ struct gnbd_request *gr_next;
+ unsigned char *gr_buf;
+ ssize_t gr_size;
+ ssize_t gr_done;
+ unsigned long gr_cookie;
+};
+
+struct gnbd_handle {
+ int gh_fd;
+ unsigned int gh_flags;
+ uint64_t gh_sectors;
+ char gh_devname[32];
+ char gh_nodename[65];
+ struct sockaddr_in gh_sin;
+ struct gnbd_request *gh_outstanding_requests;
+ struct gnbd_request **gh_outstanding_requests_last;
+ struct gnbd_request *gh_incoming_request;
+ unsigned long gh_finished_request;
+};
+#define GHF_EXPECT_KILL_GSERV_REPLY 0x0001
+#define GHF_EXPECT_LOGIN_REPLY 0x0002
+#define GHF_INCOMING_REQUEST 0x0004
+
+struct device_req {
+ char name[32];
+};
+
+struct node_req {
+ char node_name[65];
+};
+
+struct login_req {
+ uint64_t timestamp;
+ uint16_t version;
+ uint8_t pad[6];
+ char devname[32];
+};
+
+struct login_reply {
+ uint64_t sectors;
+ uint16_t version;
+ uint8_t err;
+ uint8_t pad[5];
+};
+
+struct gnbd_server_request {
+ uint32_t magic;
+ uint32_t type;
+ char handle[8];
+ uint64_t from;
+ uint32_t len;
+} __attribute__ ((packed));
+
+struct gnbd_server_reply {
+ uint32_t magic;
+ uint32_t error;
+ char handle[8];
+} __attribute__ ((packed));
+
+static int
+read_buf(int fd, void *buf, size_t count, size_t *read_count)
+{
+ int err;
+
+ err = read(fd, buf, count);
+ if (read_count) {
+ if (err >= 0)
+ *read_count = err;
+ } else if (err != count)
+ return EINTR; /* xxx */
+ return err < 0;
+}
+
+static int
+read_4(int fd, unsigned long *val)
+{
+ unsigned long buf;
+ int err;
+
+ err = read_buf(fd, &buf, sizeof(buf), NULL);
+ if (err == 0)
+ *val = ntohl(buf);
+ return err;
+}
+
+static int
+write_buf(int fd, void *buf, size_t count)
+{
+ int err;
+
+ err = write(fd, buf, count);
+ return err < 0;
+}
+
+static int
+write_4(int fd, unsigned long val)
+{
+ unsigned long buf;
+ int err;
+
+ buf = htonl(val);
+ err = write_buf(fd, &buf, sizeof(buf));
+ return err;
+}
+
+
+static int
+socket_connect(struct gnbd_handle *gh)
+{
+ int err;
+
+ if (gh->gh_fd >= 0)
+ return 0;
+
+ gh->gh_fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (gh->gh_fd < 0) {
+ warn("socket");
+ return gh->gh_fd;
+ }
+
+ err = connect(gh->gh_fd, (struct sockaddr *)&gh->gh_sin,
+ sizeof(gh->gh_sin));
+ if (err) {
+ warn("connect");
+ goto out;
+ }
+
+ return 0;
+ out:
+ close (gh->gh_fd);
+ gh->gh_fd = -1;
+ return err;
+}
+
+static int
+socket_shutdown(struct gnbd_handle *gh)
+{
+
+ close (gh->gh_fd);
+ gh->gh_fd = -1;
+ return 0;
+}
+
+static int
+find_request(struct gnbd_handle *gh, struct gnbd_request *gr)
+{
+ struct gnbd_request **tmp;
+
+ for (tmp = &gh->gh_outstanding_requests; *tmp;
+ tmp = &(*tmp)->gr_next) {
+ if (*tmp == gr) {
+ *tmp = (*tmp)->gr_next;
+ if (*tmp == NULL)
+ gh->gh_outstanding_requests_last = tmp;
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+static int
+kill_gserv(struct gnbd_handle *gh)
+{
+ struct device_req dr;
+ struct node_req nr;
+ int err;
+
+ DPRINTF(("gnbd_kill_gserv\n"));
+ err = socket_connect(gh);
+ if (err) {
+ warnx("socket_connect");
+ return err;
+ }
+
+ err = write_4(gh->gh_fd, EXTERN_KILL_GSERV_REQ);
+ if (err) {
+ warnx("send EXTERN_LOGIN_REQ failed");
+ goto out;
+ }
+
+ strncpy(dr.name, gh->gh_devname, sizeof(dr.name));
+ err = write_buf(gh->gh_fd, &dr, sizeof(dr));
+ if (err) {
+ warnx("send device_req failed");
+ goto out;
+ }
+
+ strncpy(nr.node_name, gh->gh_nodename, sizeof(nr.node_name));
+ err = write_buf(gh->gh_fd, &nr, sizeof(nr));
+ if (err) {
+ warnx("send node_req failed");
+ goto out;
+ }
+
+ gh->gh_flags |= GHF_EXPECT_KILL_GSERV_REPLY;
+ DPRINTF(("gnbd_kill_gserv ok\n"));
+
+ return 0;
+ out:
+ socket_shutdown(gh);
+ return err;
+}
+
+static int
+login(struct gnbd_handle *gh)
+{
+ struct login_req lr;
+ struct node_req nr;
+ int err;
+ uint64_t timestamp;
+ struct timeval tv;
+
+ DPRINTF(("gnbd_login\n"));
+ err = socket_connect(gh);
+ if (err) {
+ warnx("socket_connect");
+ return err;
+ }
+
+ err = write_4(gh->gh_fd, EXTERN_LOGIN_REQ);
+ if (err) {
+ warnx("send EXTERN_LOGIN_REQ failed");
+ goto out;
+ }
+
+ err = gettimeofday(&tv, NULL);
+ if (err) {
+ warnx("gettimeofday");
+ goto out;
+ }
+ timestamp = (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+
+ lr.timestamp = htonll(timestamp);
+ lr.version = htons(PROTOCOL_VERSION);
+ strncpy(lr.devname, gh->gh_devname, sizeof(lr.devname));
+ err = write_buf(gh->gh_fd, &lr, sizeof(lr));
+ if (err) {
+ warnx("send login_req failed");
+ goto out;
+ }
+
+ strncpy(nr.node_name, gh->gh_nodename, sizeof(nr.node_name));
+ err = write_buf(gh->gh_fd, &nr, sizeof(nr));
+ if (err) {
+ warnx("send node_req failed");
+ goto out;
+ }
+
+ gh->gh_flags |= GHF_EXPECT_LOGIN_REPLY;
+
+ DPRINTF(("gnbd_login ok\n"));
+ return 0;
+ out:
+ socket_shutdown(gh);
+ return err;
+}
+
+static int
+kill_gserv_reply(struct gnbd_handle *gh)
+{
+ unsigned long reply;
+ int err;
+
+ DPRINTF(("read gnbd_kill_gserv_reply\n"));
+ err = read_4(gh->gh_fd, &reply);
+ if (err) {
+ warnx("read kill_gserv_reply failed");
+ return err;
+ }
+
+ if (reply && reply != ENODEV) {
+ warnx("kill gserv failed: %s", strerror(reply));
+ return reply;
+ }
+
+ gh->gh_flags &= ~GHF_EXPECT_KILL_GSERV_REPLY;
+ socket_shutdown(gh);
+
+ err = login(gh);
+ if (err)
+ warnx("gnbd_login");
+
+ return err;
+}
+
+static int
+login_reply(struct gnbd_handle *gh)
+{
+ struct login_reply lr;
+ int err;
+
+ DPRINTF(("read gnbd_login_reply\n"));
+ err = read_buf(gh->gh_fd, &lr, sizeof(lr), NULL);
+ if (err) {
+ warnx("read login_reply failed");
+ return err;
+ }
+
+ if (lr.err) {
+ if (lr.version) {
+ warnx("gnbd version mismatch %04x != %04x",
+ PROTOCOL_VERSION, ntohs(lr.version));
+ return EINVAL;
+ }
+ warnx("login refused: %s", strerror(lr.err));
+ return lr.err;
+ }
+ gh->gh_sectors = ntohll(lr.sectors);
+
+ gh->gh_flags &= ~GHF_EXPECT_LOGIN_REPLY;
+
+ return GNBD_LOGIN_DONE;
+}
+
+static int
+incoming_request(struct gnbd_handle *gh)
+{
+ struct gnbd_request *gr = gh->gh_incoming_request;
+ ssize_t done;
+ int err;
+
+ DPRINTF(("incoming_request: done %d size %d\n", gr->gr_done,
+ gr->gr_size));
+ err = read_buf(gh->gh_fd, gr->gr_buf + gr->gr_done,
+ gr->gr_size - gr->gr_done, &done);
+ if (err)
+ goto out;
+
+ DPRINTF(("incoming_request: got %d\n", done));
+ gr->gr_done += done;
+ if (gr->gr_done == gr->gr_size) {
+ gh->gh_flags &= ~GHF_INCOMING_REQUEST;
+ gh->gh_finished_request = gr->gr_cookie;
+ free(gr);
+ return GNBD_REQUEST_DONE;
+ }
+
+ return GNBD_CONTINUE;
+
+ out:
+ gh->gh_flags &= ~GHF_INCOMING_REQUEST;
+ gh->gh_finished_request = 0;
+ free(gr);
+ return err;
+}
+
+
+
+int
+gnbd_close(struct gnbd_handle *gh)
+{
+ int err;
+ struct gnbd_request **tmp;
+
+ for (tmp = &gh->gh_outstanding_requests; *tmp; tmp = &(*tmp)->gr_next)
+ free(*tmp);
+
+ if (gh->gh_flags & GHF_INCOMING_REQUEST)
+ free(gh->gh_incoming_request);
+
+ err = close(gh->gh_fd);
+ if (err)
+ warnx("close");
+ free(gh);
+
+ return err;
+}
+
+int
+gnbd_fd(struct gnbd_handle *gh)
+{
+ return gh->gh_fd;
+}
+
+unsigned long
+gnbd_finished_request(struct gnbd_handle *gh)
+{
+ return gh->gh_finished_request;
+}
+
+int
+gnbd_read(struct gnbd_handle *gh, uint64_t sector, ssize_t count,
+ unsigned char *buf, unsigned long cookie)
+{
+ struct gnbd_server_request gsr;
+ struct gnbd_request *gr;
+ int err;
+
+ gr = malloc(sizeof(struct gnbd_request));
+ if (gr == NULL)
+ return ENOMEM;
+ memset(gr, 0, sizeof(gr));
+
+ gr->gr_buf = buf;
+ gr->gr_size = count << 9;
+ gr->gr_done = 0;
+ gr->gr_cookie = cookie;
+
+ gsr.magic = htonl(GNBD_REQUEST_MAGIC);
+ gsr.type = htonl(GNBD_CMD_READ);
+ gsr.from = htonll(sector << 9);
+ gsr.len = htonl(gr->gr_size);
+ memset(gsr.handle, 0, sizeof(gsr.handle));
+ memcpy(gsr.handle, &gr, sizeof(gr));
+
+ err = write_buf(gh->gh_fd, &gsr, sizeof(gsr));
+ if (err) {
+ warnx("write_buf");
+ goto out;
+ }
+
+ *gh->gh_outstanding_requests_last = gr;
+ gh->gh_outstanding_requests_last = &gr->gr_next;
+
+ return 0;
+
+ out:
+ free(gr);
+ return err;
+}
+
+int
+gnbd_write(struct gnbd_handle *gh, uint64_t sector, ssize_t count,
+ unsigned char *buf, unsigned long cookie)
+{
+ struct gnbd_server_request gsr;
+ struct gnbd_request *gr;
+ int err;
+
+ gr = malloc(sizeof(struct gnbd_request));
+ if (gr == NULL)
+ return ENOMEM;
+ memset(gr, 0, sizeof(gr));
+
+ gr->gr_buf = buf;
+ gr->gr_size = count << 9;
+ gr->gr_done = 0;
+ gr->gr_cookie = cookie;
+
+ gsr.magic = htonl(GNBD_REQUEST_MAGIC);
+ gsr.type = htonl(GNBD_CMD_WRITE);
+ gsr.from = htonll(sector << 9);
+ gsr.len = htonl(gr->gr_size);
+ memset(gsr.handle, 0, sizeof(gsr.handle));
+ memcpy(gsr.handle, &gr, sizeof(gr));
+
+ err = write_buf(gh->gh_fd, &gsr, sizeof(gsr));
+ if (err) {
+ warnx("write_buf");
+ goto out;
+ }
+
+ /* XXX handle non-blocking socket */
+ err = write_buf(gh->gh_fd, buf, gr->gr_size);
+ if (err) {
+ warnx("write_buf");
+ goto out;
+ }
+ gr->gr_done += gr->gr_size;
+
+ *gh->gh_outstanding_requests_last = gr;
+ gh->gh_outstanding_requests_last = &gr->gr_next;
+
+ DPRINTF(("write done\n"));
+
+ return 0;
+
+ out:
+ free(gr);
+ return err;
+}
+
+int
+gnbd_reply(struct gnbd_handle *gh)
+{
+ struct gnbd_server_reply gsr;
+ struct gnbd_request *gr;
+ int err;
+
+ DPRINTF(("gnbd_reply flags %x\n", gh->gh_flags));
+ if ((gh->gh_flags & GHF_EXPECT_KILL_GSERV_REPLY))
+ return kill_gserv_reply(gh);
+ if ((gh->gh_flags & GHF_EXPECT_LOGIN_REPLY))
+ return login_reply(gh);
+ if ((gh->gh_flags & GHF_INCOMING_REQUEST))
+ return incoming_request(gh);
+
+ DPRINTF(("read response\n"));
+ err = read_buf(gh->gh_fd, &gsr, sizeof(gsr), NULL);
+ if (err) {
+ warnx("read gnbd_reply failed");
+ return err;
+ }
+
+ if (ntohl(gsr.error)) {
+ warnx("gnbd server reply error: %s", strerror(gsr.error));
+ return gsr.error;
+ }
+
+ switch (ntohl(gsr.magic)) {
+ case GNBD_KEEP_ALIVE_MAGIC:
+ DPRINTF(("read keep alive magic\n"));
+ return GNBD_CONTINUE;
+ case GNBD_REPLY_MAGIC:
+ DPRINTF(("read reply magic\n"));
+ memcpy(&gr, gsr.handle, sizeof(gr));
+ err = find_request(gh, gr);
+ if (err) {
+ warnx("unknown request");
+ return err;
+ }
+ if (gr->gr_done != gr->gr_size) {
+ gh->gh_incoming_request = gr;
+ gh->gh_flags |= GHF_INCOMING_REQUEST;
+ return GNBD_CONTINUE;
+ } else {
+ gh->gh_finished_request = gr->gr_cookie;
+ free(gr);
+ return GNBD_REQUEST_DONE;
+ }
+ default:
+ break;
+ }
+
+ return GNBD_CONTINUE;
+}
+
+uint64_t
+gnbd_sectors(struct gnbd_handle *gh)
+{
+
+ return gh->gh_sectors;
+}
+
+struct gnbd_handle *
+gnbd_setup(char *server, unsigned int port, char *devname, char *nodename)
+{
+ struct gnbd_handle *gh;
+ struct addrinfo *res, *ai;
+ int err;
+
+ gh = malloc(sizeof(struct gnbd_handle));
+ if (gh == NULL)
+ return NULL;
+ memset(gh, 0, sizeof(gh));
+ gh->gh_fd = -1;
+ gh->gh_outstanding_requests_last = &gh->gh_outstanding_requests;
+
+ strncpy(gh->gh_devname, devname, sizeof(gh->gh_devname));
+ strncpy(gh->gh_nodename, nodename, sizeof(gh->gh_nodename));
+
+ err = getaddrinfo(server, NULL, NULL, &res);
+ if (err) {
+ if (err != EAI_SYSTEM)
+ warnx("getaddrinfo: %s", gai_strerror(err));
+ else
+ warn("getaddrinfo: %s", gai_strerror(err));
+ goto out;
+ }
+
+ for (ai = res; ai; ai = ai->ai_next) {
+ if (ai->ai_socktype != SOCK_STREAM)
+ continue;
+ if (ai->ai_family == AF_INET)
+ break;
+ }
+
+ if (ai == NULL)
+ goto out;
+
+ gh->gh_sin.sin_family = ai->ai_family;
+ gh->gh_sin.sin_port = htons(port);
+ memcpy(&gh->gh_sin.sin_addr,
+ &((struct sockaddr_in *)ai->ai_addr)->sin_addr,
+ sizeof(gh->gh_sin.sin_addr));
+
+ err = kill_gserv(gh);
+ if (err) {
+ warnx("gnbd_kill_gserv");
+ goto out;
+ }
+
+ freeaddrinfo(res);
+ return gh;
+ out:
+ free(gh);
+ freeaddrinfo(res);
+ return NULL;
+}
diff --git a/tools/blktap/libgnbd/libgnbd.h b/tools/blktap/libgnbd/libgnbd.h
new file mode 100644
index 0000000000..9fb3dbbd5f
--- /dev/null
+++ b/tools/blktap/libgnbd/libgnbd.h
@@ -0,0 +1,25 @@
+/* libgnbd.h
+ *
+ * gnbd client library
+ *
+ * Copyright (c) 2005, Christian Limpach
+ */
+
+#define GNBD_LOGIN_DONE 0x10001
+#define GNBD_REQUEST_DONE 0x10002
+#define GNBD_CONTINUE 0x10003
+#define GNBD_CONTINUE_WRITE 0x10004
+
+struct gnbd_handle;
+int gnbd_close(struct gnbd_handle *);
+int gnbd_fd(struct gnbd_handle *);
+unsigned long gnbd_finished_request(struct gnbd_handle *);
+int gnbd_kill_gserv(struct gnbd_handle *);
+int gnbd_login(struct gnbd_handle *);
+int gnbd_read(struct gnbd_handle *, uint64_t, ssize_t, unsigned char *,
+ unsigned long);
+int gnbd_write(struct gnbd_handle *, uint64_t, ssize_t, unsigned char *,
+ unsigned long);
+int gnbd_reply(struct gnbd_handle *);
+uint64_t gnbd_sectors(struct gnbd_handle *);
+struct gnbd_handle *gnbd_setup(char *, unsigned int, char *, char *);
diff --git a/tools/check/Makefile b/tools/check/Makefile
new file mode 100644
index 0000000000..744f0983b5
--- /dev/null
+++ b/tools/check/Makefile
@@ -0,0 +1,16 @@
+
+all: build
+
+# Check this machine is OK for building on.
+build:
+ ./chk build
+
+# Check this machine is OK for installing on.
+# DO NOT use this check from 'make install' in the parent
+# directory, as that target can be used to make an installable
+# copy rather than actually installing.
+install:
+ ./chk install
+
+clean:
+ ./chk clean \ No newline at end of file
diff --git a/tools/check/README b/tools/check/README
new file mode 100644
index 0000000000..e6f105b073
--- /dev/null
+++ b/tools/check/README
@@ -0,0 +1,20 @@
+Checks for the suitability of a machine for Xen build or install.
+To check for build suitability use
+
+ ./chk build
+
+To check for install suitability use
+
+ ./chk install
+
+The chk script will run checks in this directory and print
+the ones that failed. It prints nothing if checks succeed.
+The chk script exits with 0 on success and 1 on failure.
+
+The chk script runs executable files in this directory whose
+names begin with 'check_'. Files containing CHECK-BUILD
+are run for the build check, and files containing CHECK-INSTALL
+are run for the install check.
+
+Detailed output from the check scripts is in .chkbuild for build
+and .chkinstall for install. \ No newline at end of file
diff --git a/tools/check/check_brctl b/tools/check/check_brctl
new file mode 100755
index 0000000000..26f3ce6856
--- /dev/null
+++ b/tools/check/check_brctl
@@ -0,0 +1,9 @@
+#!/bin/bash
+# CHECK-INSTALL
+
+function error {
+ echo 'Check for the bridge control utils (brctl) failed.'
+ exit 1
+}
+
+brctl show || error \ No newline at end of file
diff --git a/tools/check/check_curl_devel b/tools/check/check_curl_devel
new file mode 100755
index 0000000000..342ff42bf6
--- /dev/null
+++ b/tools/check/check_curl_devel
@@ -0,0 +1,11 @@
+#!/bin/bash
+# CHECK-BUILD
+
+function error {
+ echo 'Check for libcurl includes failed.'
+ exit 1
+}
+
+set -e
+[ -e /usr/include/curl ] || error
+[ -e /usr/include/curl/curl.h ] || error \ No newline at end of file
diff --git a/tools/check/check_curl_lib b/tools/check/check_curl_lib
new file mode 100755
index 0000000000..58b74ed9c8
--- /dev/null
+++ b/tools/check/check_curl_lib
@@ -0,0 +1,10 @@
+#!/bin/bash
+# CHECK-BUILD CHECK-INSTALL
+
+function error {
+ echo 'Check for CURL library failed.'
+ exit 1
+}
+
+set -e
+ldconfig -p | grep libcurl.so || error \ No newline at end of file
diff --git a/tools/check/check_logging b/tools/check/check_logging
new file mode 100755
index 0000000000..941ba618bf
--- /dev/null
+++ b/tools/check/check_logging
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+# -*- mode: python; -*-
+
+import os
+import sys
+
+def hline():
+ print >>sys.stderr, "*" * 70
+
+def msg(message):
+ print >>sys.stderr, "*" * 3, message
+
+def check_logging():
+ """Check python logging is installed and raise an error if not.
+ Logging is standard from Python 2.3 on.
+ """
+ try:
+ import logging
+ except ImportError:
+ hline()
+ msg("Python logging is not installed.")
+ msg("Use 'make install-logging' at the xen root to install.")
+ msg("")
+ msg("Alternatively download and install from")
+ msg("http://www.red-dove.com/python_logging.html")
+ hline()
+ sys.exit(1)
+
+if __name__ == '__main__':
+ check_logging()
diff --git a/tools/check/check_python b/tools/check/check_python
new file mode 100755
index 0000000000..321b32dbbe
--- /dev/null
+++ b/tools/check/check_python
@@ -0,0 +1,10 @@
+#!/bin/bash
+# CHECK-BUILD CHECK-INSTALL
+
+function error {
+ echo "Check for Python version 2.2 or higher failed."
+ exit 1
+}
+
+python -V
+python -V 2>&1 | cut -d ' ' -f 2 | grep -q -E '^2.2|^2.3|^2.4' || error
diff --git a/tools/check/check_twisted b/tools/check/check_twisted
new file mode 100755
index 0000000000..06d70b4692
--- /dev/null
+++ b/tools/check/check_twisted
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# CHECK-INSTALL
+# -*- mode: python; -*-
+
+import os
+import sys
+
+def hline():
+ print >>sys.stderr, "*" * 70
+
+def msg(message):
+ print >>sys.stderr, "*" * 3, message
+
+def check_twisted_version():
+ """Check twisted is installed with a supported version and print a warning if not.
+ Raises an error if twisted is not installed.
+ """
+ # Supported twisted release and major version.
+ RELEASE = 1
+ MAJOR = 3
+ try:
+ from twisted.copyright import version
+ except ImportError:
+ hline()
+ msg("The Twisted framework is not installed.")
+ msg("Use 'make install-twisted' at the xen root to install.")
+ msg("")
+ msg("Alternatively download and install version %d.%d or higher" % (RELEASE, MAJOR))
+ msg("from http://www.twistedmatrix.com/products")
+ hline()
+ sys.exit(1)
+
+ (release, major, minor) = version.split('.')
+ release = int(release)
+ major = int(major)
+ if release > RELEASE: return
+ if release == RELEASE and major >= MAJOR: return
+ hline()
+ msg("Warning: Twisted version not supported: %s" % version)
+ msg("Use Twisted version %d.%d.0 or higher" % (RELEASE, MAJOR))
+ hline()
+ sys.exit(1)
+
+if __name__ == '__main__':
+ check_twisted_version()
+
diff --git a/tools/check/check_zlib_devel b/tools/check/check_zlib_devel
new file mode 100755
index 0000000000..6f6334bcd0
--- /dev/null
+++ b/tools/check/check_zlib_devel
@@ -0,0 +1,10 @@
+#!/bin/bash
+# CHECK-BUILD
+
+function error {
+ echo 'Check for zlib includes failed.'
+ exit 1
+}
+
+set -e
+[ -e /usr/include/zlib.h ] || error \ No newline at end of file
diff --git a/tools/check/check_zlib_lib b/tools/check/check_zlib_lib
new file mode 100755
index 0000000000..4e19372843
--- /dev/null
+++ b/tools/check/check_zlib_lib
@@ -0,0 +1,10 @@
+#!/bin/bash
+# CHECK-BUILD CHECK-INSTALL
+
+function error {
+ echo 'Check for zlib library failed.'
+ exit 1
+}
+
+set -e
+ldconfig -p | grep libz.so || error \ No newline at end of file
diff --git a/tools/check/chk b/tools/check/chk
new file mode 100755
index 0000000000..0ebce4799e
--- /dev/null
+++ b/tools/check/chk
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+function usage {
+ echo "Usage:"
+ echo "\t$0 [build|install|clean]"
+ echo
+ echo "Check suitability for Xen build or install."
+ echo "Exit with 0 if OK, 1 if not."
+ echo "Prints only failed tests."
+ echo
+ echo "Calling with 'clean' removes generated files."
+ exit 1
+}
+
+export PATH=${PATH}:/sbin:/usr/sbin
+
+case $1 in
+ build)
+ check="CHECK-BUILD"
+ info=".chkbuild"
+ ;;
+ install)
+ check="CHECK-INSTALL"
+ info=".chkinstall"
+ ;;
+ clean)
+ rm -f .chkbuild .chkinstall
+ exit 0
+ ;;
+ *)
+ usage
+ ;;
+esac
+
+failed=0
+
+echo "Xen ${check} " $(date) > ${info}
+for f in check_* ; do
+ case $f in
+ *~)
+ continue
+ ;;
+ *)
+ ;;
+ esac
+ if ! [ -x $f ] ; then
+ continue
+ fi
+ if ! grep -q ${check} $f ; then
+ continue
+ fi
+ echo ' ' >> ${info}
+ echo "Checking $f" >> ${info}
+ if ./$f 1>>${info} 2>&1 ; then
+ echo OK >> ${info}
+ else
+ failed=1
+ echo "FAILED $f"
+ echo FAILED >> ${info}
+ fi
+done
+
+echo >> ${info}
+
+if [ "$failed" == "1" ] ; then
+ echo "Checks failed. See `pwd`/${info} for details."
+ echo "FAILED" >> ${info}
+ exit 1
+else
+ echo "OK" >> ${info}
+ exit 0
+fi
diff --git a/tools/examples/Makefile b/tools/examples/Makefile
index f7c3fd7420..ae65b6748f 100644
--- a/tools/examples/Makefile
+++ b/tools/examples/Makefile
@@ -1,35 +1,53 @@
-
-INSTALL = $(wildcard *.py)
-
-INITD = init.d/xend
-
-XEN_CONFIG_DIR = /etc/xen
-XEN_CONFIGS = xmdefaults xmnetbsd xend-config.sxp
-
-XEN_SCRIPT_DIR = /etc/xen
-XEN_SCRIPTS = vifctl network vif-bridge
+INSTALL = install
+INSTALL_DIR = $(INSTALL) -d -m0755
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DATA = $(INSTALL) -m0644
+
+# Init scripts.
+XEND_INITD = init.d/xend
+XENDOMAINS_INITD = init.d/xendomains
+
+# Xen configuration dir and configs to go there.
+XEN_CONFIG_DIR = /etc/xen
+XEN_CONFIGS = xend-config.sxp
+XEN_CONFIGS += xmexample1
+XEN_CONFIGS += xmexample2
+XEN_CONFIGS += xmexample.vmx
+XEN_CONFIGS += mem-map.sxp
+XEN_CONFIGS += bochsrc
+
+# Xen script dir and scripts to go there.
+XEN_SCRIPT_DIR = /etc/xen/scripts
+XEN_SCRIPTS = network vif-bridge
+XEN_SCRIPTS += network-route vif-route
+XEN_SCRIPTS += block-file
+XEN_SCRIPTS += block-enbd
all:
install: all install-initd install-configs install-scripts
install-initd:
- mkdir -p $(prefix)/etc/init.d
- install -m0755 $(INITD) $(prefix)/etc/init.d
+ [ -d $(DESTDIR)/etc/init.d ] || $(INSTALL_DIR) $(DESTDIR)/etc/init.d
+ $(INSTALL_PROG) $(XEND_INITD) $(DESTDIR)/etc/init.d
+ $(INSTALL_PROG) $(XENDOMAINS_INITD) $(DESTDIR)/etc/init.d
install-configs:
- mkdir -p $(prefix)$(XEN_CONFIG_DIR)
- mkdir -p $(prefix)$(XEN_CONFIG_DIR)/auto
+ [ -d $(DESTDIR)$(XEN_CONFIG_DIR) ] || \
+ $(INSTALL_DIR) $(DESTDIR)$(XEN_CONFIG_DIR)
+ [ -d $(DESTDIR)$(XEN_CONFIG_DIR)/auto ] || \
+ $(INSTALL_DIR) $(DESTDIR)$(XEN_CONFIG_DIR)/auto
for i in $(XEN_CONFIGS); \
- do [ -a $(prefix)/$(XEN_CONFIG_DIR)/$$i ] || \
- install -m0644 $$i $(prefix)$(XEN_CONFIG_DIR); \
+ do [ -a $(DESTDIR)$(XEN_CONFIG_DIR)/$$i ] || \
+ $(INSTALL_DATA) $$i $(DESTDIR)$(XEN_CONFIG_DIR); \
done
install-scripts:
- mkdir -p $(prefix)$(XEN_SCRIPT_DIR)
+ [ -d $(DESTDIR)$(XEN_SCRIPT_DIR) ] || \
+ $(INSTALL_DIR) $(DESTDIR)$(XEN_SCRIPT_DIR)
for i in $(XEN_SCRIPTS); \
- do [ -a $(prefix)/$()/$$i ] || \
- install -m0755 $$i $(prefix)$(XEN_SCRIPT_DIR); \
+ do [ -a $(DESTDIR)$(XEN_SCRIPT_DIR)/$$i ] || \
+ $(INSTALL_PROG) $$i $(DESTDIR)$(XEN_SCRIPT_DIR); \
done
clean:
diff --git a/tools/examples/README b/tools/examples/README
index 117f1091ef..d17c7ef9a5 100644
--- a/tools/examples/README
+++ b/tools/examples/README
@@ -1,138 +1,17 @@
-Xen Control Tools - Example Scripts
+Xen Control Tools - Examples
===================================
-This directory contains a set of example scripts for common Xen operations.
+This directory contains example scripts and configurations for Xen.
For many operations you will either be able to use these scripts directly, or
incorporate code from them into your own scripts.
-The Xc and xenctl.utils Python modules provide an API for accessing all this
-functionality - and more - from your own Python programs. These libraries may
-contain features for which there aren't yet example scripts written for...
-
If you write a useful script and would like to share it, please do
send it (preferably with a little summary to go in this file) to
<xen-devel@lists.sourceforge.net> so we can add it to this directory.
-xc_dom_control.py
- - general tool for controling running domains
- Usage: xc_dom_control.py [command] <params>
-
- stop [dom] -- pause a domain
- start [dom] -- un-pause a domain
- shutdown [dom] [[-w]] -- request a domain to shutdown (can specify 'all')
- (optionally wait for complete shutdown)
- destroy [dom] -- immediately terminate a domain
- pincpu [dom] [cpu] -- pin a domain to the specified CPU
- suspend [dom] [file] -- write domain's memory to a file and terminate
- (resume by re-running xc_dom_create with -L option)
- unwatch [dom] -- kill the auto-restart daemon for a domain
- list -- print info about all domains
- listvbds -- print info about all virtual block devs
- cpu_bvtset [dom] [mcuadv] [warp] [warpl] [warpu]
- -- set BVT scheduling parameters for domain
- cpu_bvtslice [slice] -- set default BVT scheduler slice
- cpu_atropos_set [dom] [period] [slice] [latency] [xtratime]
- -- set Atropos scheduling parameters for domain
- cpu_rrobin_slice [slice] -- set Round Robin scheduler slice
- vif_stats [dom] [vif] -- get stats for a given network vif
- vif_addip [dom] [vif] [ip] -- add an IP address to a given vif
- vif_setsched [dom] [vif] [bytes] [usecs] -- rate limit vif bandwidth
- vif_getsched [dom] [vif] -- print vif's scheduling parameters
- vbd_add [dom] [uname] [dev] [mode] -- make disk/partition uname available to
- domain as dev e.g. 'vbd_add 2 phy:sda3 hda1 w'
- vbd_remove [dom] [dev] -- remove disk or partition attached as 'dev'
-
-
-xc_dom_create.py
- - This tool is used to create and start new domains. It reads defaults
-from a file written in Python, having allowed variables to be set and
-passed into the file. Further command line arguments allow the
-defaults to be overridden. The defaults for each parameter are listed
-in [] brackets. Arguments are as follows:
-
-Arguments to control the parsing of the defaults file:
- -f config_file -- Use the specified defaults script.
- Default: ['/etc/xc/defaults']
- -L state_file -- Load virtual machine memory state from state_file
- -D foo=bar -- Set variable foo=bar before parsing config
- E.g. '-D vmid=3;ip=1.2.3.4'
- -h -- Print extended help message, including all arguments
- -n -- Dry run only, don't actually create domain
- -q -- Quiet - write output only to the system log
-
-
-The config file 'defaults' requires the following variable to be defined:
- vmid -- Numeric identifier for the new domain, used to calculate
- the VM's IP address and root partition. E.g. -Dvmid=1
-
-
-Arguments to override current config read from 'defaults':
- -k image -- Path to kernel image ['']
- -r ramdisk -- Path to ramdisk (or empty) ['']
- -b builder_fn -- Function to use to build domain ['']
- -m mem_size -- Initial memory allocation in MB [0MB]
- -N domain_name -- Set textual name of domain ['']
- -a auto_restart -- Restart domain on exit, yes/no ['0']
- -e vbd_expert -- Saftey catch to avoid some disk accidents ['0']
- -d udisk,dev,rw -- Add disk, partition, or virtual disk to domain. E.g. to
- make partion sda4 available to the domain as hda1 with
- read-write access: '-b phy:sda4,hda1,rw' To add
- multiple disks use multiple -d flags or seperate with ';'
- Default: ['']
- -i vfr_ipaddr -- Add IP address to the list which Xen will route to
- the domain. Use multiple times to add more IP addrs.
- Default: ['']
-
-Args to override the kernel command line, which is concatenated from these:
- -I cmdline_ip -- Override 'ip=ipaddr:nfsserv:gateway:netmask::eth0:off'
- Default: ['']
- -R cmdline_root -- Override root device parameters.
- Default: ['']
- -E cmdline_extra -- Override extra kernel args and rc script env vars.
- Default: ['']
-
-
-
-xc_vd_tool.py
- - tool for manipulating virtual disks
- Usage: xc_vd_tool command <params>
-
- initalise [dev] [[ext_size]] - init. a physcial partition to store vd's
- create [size] [[expiry]] - allocate a vd of specified size (and expiry)
- delete [vdid] - delete a vd
- import [filename] [[expiry]] - create a vd and populate w/ image from file
- export [vdid] [filename] - copy vd's contents to a file
- setexpiry [vdid] [[expiry]] - update the expiry time for a vd
- list - list all the unexpired virtual disks
- undelete [vdid] [[expiry]] - attempts to recover an expired vd
- freespace - print out the amount of space in free pool
-
- notes:
- vdid - the virtual disk's identity string
- size - measured in MB
- expiry - is the expiry time of the virtual disk in seconds from now
- (0 = don't expire)
- device - physical partition to 'format' to hold vd's. e.g. hda4
- ext_size - extent size (default 64MB)
-
-
-xendomains
-This is a Sys-V init script for RedHat systems.
-
- - Usage: xendomains {start|stop|status}
-
- start -- starts all the domains with config files in /etc/xc/auto/
- stop -- stops ALL running domains, waiting for them to shutdown cleanly
- (if possible) before returning
- status -- prints a list of the running domains, the same as
- "xc_dom_control.py list"
-
-On a RedHat system it should be possible to issue commands to this
-script using the "service" command and to configure if / when it is
-run automatically, using the "chkconfig" command.
-
-xend
-This is a Sys-V init script for RedHat systems, which can be used to
-start the Xen Daemon (xend) at boot time.
+network - default network setup script called by xend at startup.
+vif-bridge - default virtual network interface setup script.
+xend-config.sxp - default xend configuration file.
+xmexample1 - example configuration script for 'xm create'.
+xmexample2 - a more complex configuration script for 'xm create'.
- - Usage: xend {start|stop|status|restart|reload}
diff --git a/tools/examples/block-enbd b/tools/examples/block-enbd
new file mode 100755
index 0000000000..cfae6288b2
--- /dev/null
+++ b/tools/examples/block-enbd
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Usage: block-enbd [bind server ctl_port |unbind node]
+#
+# The file argument to the bind command is the file we are to bind to a
+# loop device. We print the path to the loop device node to stdout.
+#
+# The node argument to unbind is the name of the device node we are to
+# unbind.
+#
+# This assumes you're running a correctly configured server at the other end!
+
+case $1 in
+ bind)
+ for dev in /dev/nd*; do
+ if nbd-client $2:$3 $dev; then
+ echo $dev
+ exit 0
+ fi
+ done
+ exit 1
+ ;;
+
+ unbind)
+ nbd-client -d $2
+ exit 0
+ ;;
+
+ *)
+ echo 'Unknown command: ' $1
+ echo 'Valid commands are: bind, unbind'
+ exit 1
+esac
diff --git a/tools/examples/block-file b/tools/examples/block-file
new file mode 100755
index 0000000000..362b1faee5
--- /dev/null
+++ b/tools/examples/block-file
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Usage: block_loop [bind file|unbind node]
+#
+# The file argument to the bind command is the file we are to bind to a
+# loop device. We print the path to the loop device node to stdout.
+#
+# The node argument to unbind is the name of the device node we are to
+# unbind.
+
+case $1 in
+ bind)
+ for dev in /dev/loop*; do
+ if losetup $dev $2; then
+ echo $dev
+ exit 0
+ fi
+ done
+ exit 1
+ ;;
+
+ unbind)
+ losetup -d $2
+ exit 0
+ ;;
+
+ *)
+ echo 'Unknown command: ' $1
+ echo 'Valid commands are: bind, unbind'
+ exit 1
+esac
diff --git a/tools/examples/bochsrc b/tools/examples/bochsrc
new file mode 100644
index 0000000000..d80884b3c8
--- /dev/null
+++ b/tools/examples/bochsrc
@@ -0,0 +1,20 @@
+#megs: 32
+#romimage: file=$BXSHARE/BIOS-bochs-latest, address=0xf0000
+#vgaromimage: $BXSHARE/VGABIOS-lgpl-latest
+floppya: 1_44=a.img, status=inserted
+floppyb: 1_44=b.img, status=inserted
+# if you don't use absolute paths below, bochs looks under the cwd of xend,
+# which is usually "/"
+#ata0-master: type=disk, path=/var/images/min-el3-i386.img, cylinders=800, heads=4, spt=32
+i440fxsupport: enabled=1
+ne2k: ioaddr=0x300, irq=9, mac=b0:c4:22:01:00:00, ethmod=linux, ethdev=eth0
+ata0-master: type=disk, path=/var/images/1g-el3-i386.img, mode=flat, cylinders=2048, heads=16, spt=63
+boot: c
+
+log: /tmp/bochsout.txt
+#debug: action=report
+info: action=report
+error: action=report
+panic: action=ask
+
+mouse: enabled=0
diff --git a/tools/examples/init.d/xend b/tools/examples/init.d/xend
index 2e163045ee..fc94c17dd2 100755
--- a/tools/examples/init.d/xend
+++ b/tools/examples/init.d/xend
@@ -4,28 +4,44 @@
#
# Author: Keir Fraser <keir.fraser@cl.cam.ac.uk>
#
-# chkconfig: 2345 99 00
+# chkconfig: 2345 98 01
# description: Starts and stops the Xen control daemon.
+# Wait for Xend / Xfrd to be up
+function await_daemons_up
+{
+ i=1
+ rets=10
+ xend status
+ while [ $? -ne 0 -a $i -lt $rets ]; do
+ sleep 1
+ echo -n .
+ i=$(($i + 1))
+ xend status
+ done
+}
+
case "$1" in
start)
xend start
- exit $?
+ await_daemons_up
;;
stop)
xend stop
- exit $?
;;
status)
+ xend status
;;
- restart|reload)
+ restart|reload|force-reload)
+ xend restart
+ await_daemons_up
;;
*)
# do not advertise unreasonable commands that there is no reason
# to use with this device
- echo $"Usage: $0 {start|stop|status|restart|reload}"
+ echo $"Usage: $0 {start|stop|status|restart|reload|force-reload}"
exit 1
esac
-exit 0
+exit $?
diff --git a/tools/examples/init.d/xendomains b/tools/examples/init.d/xendomains
new file mode 100755
index 0000000000..c52c3cc0e9
--- /dev/null
+++ b/tools/examples/init.d/xendomains
@@ -0,0 +1,154 @@
+#!/bin/sh
+#
+# /etc/init.d/xendomains
+# Start / stop domains automatically when domain 0 boots / shuts down.
+#
+# chkconfig: 345 99 00
+# description: Start / stop Xen domains.
+#
+# This script offers fairly basic functionality. It should work on Redhat
+# but also on LSB-compliant SuSE releases and on Debian with the LSB package
+# installed. (LSB is the Linux Standard Base)
+#
+# Based on the example in the "Designing High Quality Integrated Linux
+# Applications HOWTO" by Avi Alkalay
+# <http://www.tldp.org/HOWTO/HighQuality-Apps-HOWTO/>
+#
+
+RETVAL=0
+
+INITD=/etc/init.d
+
+AUTODIR=/etc/xen/auto
+LOCKFILE=/var/lock/subsys/xendomains
+
+if [ -e /lib/lsb ]; then
+ # assume an LSB-compliant distro (Debian with LSB package,
+ # recent-enough SuSE, others...)
+
+ . /lib/lsb/init-functions # source LSB standard functions
+
+ on_fn_exit()
+ {
+ if [ $RETVAL -eq 0 ]; then
+ log_success_msg
+ else
+ log_failure_msg
+ fi
+ }
+elif [ -r $INITD/functions ]; then
+ # assume a Redhat-like distro
+ . $INITD/functions # source Redhat functions
+
+ on_fn_exit()
+ {
+ if [ $RETVAL -eq 0 ]; then
+ success
+ else
+ failure
+ fi
+
+ echo
+ }
+else
+ # none of the above
+ LOCKFILE=/var/lock/xendomains
+
+ on_fn_exit()
+ {
+ echo
+ }
+fi
+
+
+
+start() {
+ if [ -f $LOCKFILE ]; then return; fi
+
+ echo -n $"Starting auto Xen domains:"
+
+ # We expect config scripts for auto starting domains to be in
+ # AUTODIR - they could just be symlinks to files elsewhere
+ if [ -d $AUTODIR ] && [ $(ls $AUTODIR | wc -l) -gt 0 ]; then
+ touch $LOCKFILE
+
+ # Create all domains with config files in AUTODIR.
+ for dom in $AUTODIR/*; do
+ xm create --quiet --defconfig $dom
+ if [ $? -ne 0 ]; then
+ RETVAL=$?
+ fi
+ done
+
+ fi
+
+ on_fn_exit
+}
+
+stop()
+{
+ # NB. this shuts down ALL Xen domains (politely), not just the ones in
+ # AUTODIR/*
+ # This is because it's easier to do ;-) but arguably if this script is run
+ # on system shutdown then it's also the right thing to do.
+
+ echo -n $"Shutting down all Xen domains:"
+
+ xm shutdown --all --wait --halt
+
+ RETVAL=$?
+
+ [ $RETVAL -eq 0 ] && rm -f $LOCKFILE
+
+ on_fn_exit
+}
+
+# This does NOT necessarily restart all running domains: instead it
+# stops all running domains and then boots all the domains specified in
+# AUTODIR. If other domains have been started manually then they will
+# not get restarted.
+# Commented out to avoid confusion!
+#
+#restart()
+#{
+# stop
+# start
+#}
+
+# same as restart for now - commented out to avoid confusion
+#reload()
+#{
+# restart
+#}
+
+
+case "$1" in
+ start)
+ start
+ ;;
+
+ stop)
+ stop
+ ;;
+
+# The following are commented out to disable them by default to avoid confusion
+# - see the notes above
+#
+# restart)
+# restart
+# ;;
+#
+# reload)
+# reload
+# ;;
+
+ status)
+ xm list
+ ;;
+
+ *)
+ echo $"Usage: $0 {start|stop|status}"
+ ;;
+esac
+
+exit $RETVAL
diff --git a/tools/examples/mem-map.sxp b/tools/examples/mem-map.sxp
new file mode 100644
index 0000000000..246b49b92a
--- /dev/null
+++ b/tools/examples/mem-map.sxp
@@ -0,0 +1,10 @@
+(memmap
+ (0000000000000000 000000000009f800 "AddressRangeMemory" WB)
+ (000000000009f800 00000000000a0000 "AddressRangeReserved" UC)
+ (00000000000a0000 00000000000bffff "AddressRangeIO" UC)
+ (00000000000f0000 0000000000100000 "AddressRangeReserved" UC)
+ (0000000000100000 0000000008000000 "AddressRangeMemory" WB)
+ (0000000007fff000 0000000008000000 "AddressRangeShared" WB)
+ (0000000008000000 0000000008003000 "AddressRangeNVS" UC)
+ (0000000008003000 000000000800d000 "AddressRangeACPI" WB)
+ (00000000fec00000 0000000100000000 "AddressRangeIO" UC))
diff --git a/tools/examples/network b/tools/examples/network
index bd736e1ba5..229881d737 100755
--- a/tools/examples/network
+++ b/tools/examples/network
@@ -1,10 +1,22 @@
#!/bin/sh
#============================================================================
-# Example Xen network start/stop script.
+# Default Xen network start/stop script.
# Xend calls a network script when it starts.
-# This is the default script.
+# The script name to use is defined in /etc/xen/xend-config.sxp
+# in the network-script field.
#
-# /etc/xen/network (start|stop|status) {VAR=VAL}*
+# This script creates a bridge (default xen-br0), adds a device
+# (default eth0) to it, copies the IP addresses from the device
+# to the bridge and adjusts the routes accordingly.
+#
+# If all goes well, this should ensure that networking stays up.
+# However, some configurations are upset by this, especially
+# NFS roots. If the bridged setup does not meet your needs,
+# configure a different script, for example using routing instead.
+#
+# Usage:
+#
+# network (start|stop|status) {VAR=VAL}*
#
# Vars:
#
@@ -49,13 +61,13 @@ transfer_addrs () {
local src=$1
local dst=$2
# Don't bother if $dst already has IP addresses.
- if ip addr show dev ${dst} | egrep -q '^ *inet' ; then
+ if ip addr show dev ${dst} | egrep -q '^ *inet ' ; then
return
fi
# Address lines start with 'inet' and have the device in them.
# Replace 'inet' with 'ip addr add' and change the device name $src
# to 'dev $src'. Remove netmask as we'll add routes later.
- ip addr show dev ${src} | egrep '^ *inet' | sed -e "
+ ip addr show dev ${src} | egrep '^ *inet ' | sed -e "
s/inet/ip addr add/
s@\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)/[0-9]\+@\1@
s/${src}/dev ${dst}/
@@ -127,31 +139,44 @@ show_status () {
echo '============================================================'
}
+op_start () {
+ if [ "${bridge}" == "null" ] ; then
+ return
+ fi
+ # Create the bridge and give it the interface IP addresses.
+ # Move the interface routes onto the bridge.
+ create_bridge ${netdev} ${bridge}
+ transfer_addrs ${netdev} ${bridge}
+ transfer_routes ${netdev} ${bridge}
+ # Don't add $dev to $bridge if it's already on a bridge.
+ if ! brctl show | grep -q ${netdev} ; then
+ brctl addif ${bridge} ${netdev}
+ fi
+
+ if [ ${antispoof} == 'yes' ] ; then
+ antispoofing ${netdev} ${bridge}
+ fi
+}
+
+op_stop () {
+ if [ "${bridge}" == "null" ] ; then
+ return
+ fi
+ # Remove the interface from the bridge.
+ # Move the routes back to the interface.
+ brctl delif ${bridge} ${netdev}
+ transfer_routes ${bridge} ${netdev}
+
+ # It's not our place to be enabling forwarding...
+}
+
case ${OP} in
start)
- # Create the bridge and give it the interface IP addresses.
- # Move the interface routes onto the bridge.
- create_bridge ${netdev} ${bridge}
- transfer_addrs ${netdev} ${bridge}
- transfer_routes ${netdev} ${bridge}
- # Don't add $dev to $bridge if it's already on a bridge.
- if ! brctl show | grep -q ${netdev} ; then
- brctl addif ${bridge} ${netdev}
- fi
-
- if [ ${antispoof} == 'yes' ] ; then
- antispoofing ${netdev} ${bridge}
- fi
-
+ op_start
;;
stop)
- # Remove the interface from the bridge.
- # Move the routes back to the interface.
- brctl delif ${bridge} ${netdev}
- transfer_routes ${bridge} ${netdev}
-
- # It's not our place to be enabling forwarding...
+ op_stop
;;
status)
diff --git a/tools/examples/network-nat b/tools/examples/network-nat
new file mode 100644
index 0000000000..ed32b70c58
--- /dev/null
+++ b/tools/examples/network-nat
@@ -0,0 +1,77 @@
+#!/bin/sh
+#============================================================================
+# Default Xen network start/stop script.
+# Xend calls a network script when it starts.
+# The script name to use is defined in /etc/xen/xend-config.sxp
+# in the network-script field.
+#
+# Usage:
+#
+# network-route (start|stop|status) {VAR=VAL}*
+#
+# Vars:
+#
+# netdev The gateway interface (default eth0).
+# antispoof Whether to use iptables to prevent spoofing (default yes).
+#
+#============================================================================
+
+
+
+# Exit if anything goes wrong.
+set -e
+
+# First arg is the operation.
+OP=$1
+shift
+
+# Pull variables in args in to environment.
+for arg ; do export "${arg}" ; done
+
+netdev=${netdev:-eth0}
+# antispoofing not yet implemented
+antispoof=${antispoof:-yes}
+
+echo "network-nat $OP netdev=$netdev antispoof=$antispoof"
+
+
+op_start() {
+ echo 1 >/proc/sys/net/ipv4/ip_forward
+ iptables -t nat -A POSTROUTING -o ${netdev} -j MASQUERADE
+}
+
+
+op_stop() {
+ iptables -t nat -D POSTROUTING -o ${netdev} -j MASQUERADE
+}
+
+
+show_status() {
+ echo '============================================================'
+ ifconfig
+ echo ' '
+ ip route list
+ echo ' '
+ route -n
+ echo '============================================================'
+
+}
+
+case ${OP} in
+ start)
+ op_start
+ ;;
+
+ stop)
+ op_stop
+ ;;
+
+ status)
+ show_status
+ ;;
+
+ *)
+ echo 'Unknown command: ' ${OP}
+ echo 'Valid commands are: start, stop, status'
+ exit 1
+esac
diff --git a/tools/examples/network-route b/tools/examples/network-route
new file mode 100755
index 0000000000..cb217c068d
--- /dev/null
+++ b/tools/examples/network-route
@@ -0,0 +1,19 @@
+#!/bin/sh
+#============================================================================
+# Default Xen network start/stop script.
+# Xend calls a network script when it starts.
+# The script name to use is defined in /etc/xen/xend-config.sxp
+# in the network-script field.
+#
+# Usage:
+#
+# network-route (start|stop|status) {VAR=VAL}*
+#
+# Vars:
+#
+# netdev The gateway interface (default eth0).
+# antispoof Whether to use iptables to prevent spoofing (default yes).
+#
+#============================================================================
+
+echo 1 >/proc/sys/net/ipv4/ip_forward
diff --git a/tools/examples/vif-bridge b/tools/examples/vif-bridge
index 2138aa7b94..42bdf0e173 100755
--- a/tools/examples/vif-bridge
+++ b/tools/examples/vif-bridge
@@ -69,8 +69,14 @@ case $OP in
;;
esac
+# Don't do anything if the bridge is "null".
+if [ "${bridge}" == "null" ] ; then
+ exit
+fi
+
# Add/remove vif to/from bridge.
brctl ${brcmd} ${bridge} ${vif}
+ifconfig ${vif} $OP
if [ ${ip} ] ; then
diff --git a/tools/examples/vif-nat b/tools/examples/vif-nat
new file mode 100644
index 0000000000..4b6d348dfa
--- /dev/null
+++ b/tools/examples/vif-nat
@@ -0,0 +1,66 @@
+#!/bin/sh
+#============================================================================
+# /etc/xen/vif-nat
+#
+# Script for configuring a vif in routed-nat mode.
+# Xend calls a vif script when bringing a vif up or down.
+# This script is the default - but it can be configured for each vif.
+#
+# Example invocation:
+#
+# vif-nat up domain=VM1 vif=vif1.0 ip="192.168.0.10/31"
+#
+# Usage:
+# vif-nat (up|down) {VAR=VAL}*
+#
+# Vars:
+#
+# domain name of the domain the interface is on (required).
+# vif vif interface name (required).
+# ip list of IP networks for the vif, space-separated (required).
+#============================================================================
+
+# Exit if anything goes wrong
+set -e
+
+echo "vif-nat $*"
+
+# Operation name.
+OP=$1
+shift
+
+# Pull variables in args into environment
+for arg ; do export "${arg}" ; done
+
+# Required parameters. Fail if not set.
+domain=${domain:?}
+vif=${vif:?}
+ip=${ip:?}
+
+# strip /netmask
+vif_ip=`echo ${ip} | awk -F/ '{print $1}'`
+
+main_ip=`ifconfig eth0 | grep "inet addr:" | sed -e 's/.*inet addr:\(\w\w*\.\w\w*\.\w\w*\.\w\w*\).*/\1/'`
+
+# Are we going up or down?
+case $OP in
+ up)
+ ifconfig ${vif} ${vif_ip} netmask 255.255.255.0 up
+ echo 1 >/proc/sys/net/ipv4/conf/${vif}/proxy_arp
+ iptcmd='-A'
+ ipcmd='a'
+ ;;
+ down)
+ ifconfig ${vif} down
+ iptcmd='-D'
+ ipcmd='d'
+ ;;
+ *)
+ echo 'Invalid command: ' $OP
+ echo 'Valid commands are: up, down'
+ exit 1
+ ;;
+esac
+
+ip r ${ipcmd} ${ip} dev ${vif} src ${main_ip}
+# iptables ${iptcmd} FORWARD -m physdev --physdev-in ${vif} -p udp --sport 68 --dport 67 -j ACCEPT
diff --git a/tools/examples/vif-route b/tools/examples/vif-route
new file mode 100755
index 0000000000..b15aea1e5c
--- /dev/null
+++ b/tools/examples/vif-route
@@ -0,0 +1,76 @@
+#!/bin/sh
+#============================================================================
+# /etc/xen/vif-route
+#
+# Script for configuring a vif in routed mode.
+# Xend calls a vif script when bringing a vif up or down.
+# This script is the default - but it can be configured for each vif.
+#
+# Example invocation:
+#
+# vif-route up domain=VM1 vif=vif1.0 ip="128.232.38.45/28 10.10.10.55/24"
+#
+# Usage:
+# vif-route (up|down) {VAR=VAL}*
+#
+# Vars:
+#
+# domain name of the domain the interface is on (required).
+# vif vif interface name (required).
+# mac vif MAC address (required).
+# ip list of IP networks for the vif, space-separated (optional).
+#============================================================================
+
+# Exit if anything goes wrong
+set -e
+
+echo "vif-route $*"
+
+# Operation name.
+OP=$1
+shift
+
+# Pull variables in args into environment
+for arg ; do export "${arg}" ; done
+
+# Required parameters. Fail if not set.
+domain=${domain:?}
+vif=${vif:?}
+mac=${mac:?}
+
+# Optional parameters. Set defaults.
+ip=${ip:-''} # default to null (do nothing)
+
+main_ip=`ifconfig eth0 | grep "inet addr:" | sed -e 's/.*inet addr:\(\w\w*\.\w\w*\.\w\w*\.\w\w*\).*/\1/'`
+
+# Are we going up or down?
+case $OP in
+ up)
+ ifconfig ${vif} 169.254.1.0 netmask 255.255.255.255 up
+ echo 1 >/proc/sys/net/ipv4/conf/${vif}/proxy_arp
+ iptcmd='-A'
+ ipcmd='a'
+ ;;
+ down)
+ ifconfig ${vif} down
+ iptcmd='-D'
+ ipcmd='d'
+ ;;
+ *)
+ echo 'Invalid command: ' $OP
+ echo 'Valid commands are: up, down'
+ exit 1
+ ;;
+esac
+
+if [ ${ip} ] ; then
+
+ # If we've been given a list of IP networks, allow pkts with these src addrs.
+ for addr in ${ip} ; do
+ ip r ${ipcmd} ${addr} dev ${vif} src ${main_ip}
+# iptables ${iptcmd} FORWARD -m physdev --physdev-in ${vif} -s ${addr} -j ACCEPT
+ done
+
+ # Always allow us to talk to a DHCP server anyhow.
+# iptables ${iptcmd} FORWARD -m physdev --physdev-in ${vif} -p udp --sport 68 --dport 67 -j ACCEPT
+fi
diff --git a/tools/examples/vifctl b/tools/examples/vifctl
deleted file mode 100644
index 11aac70fa2..0000000000
--- a/tools/examples/vifctl
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/usr/bin/python
-# -*- mode: python; -*-
-#============================================================================
-# Xen vif control script. Lives in /etc/xen/xend.
-#
-# vifctl init [bridge=<bridge>] [interface=<interface>]
-#
-# Called when xend starts up. Default behaviour is to create <bridge>
-# and add <interface> to it, moving its IP address to <bridge> and adjusting routes.
-#
-# vifctl (up|down) vif=<vif> mac=<mac> [bridge=<bridge>] (ipaddr=<ipaddr>)*
-#
-# Called when a vif is brought up or down. Default behaviour is to add
-# the vif to <bridge> on up and remove it from the bridge on down.
-# If ipaddr is specified, iptables rules for the ip addresses are
-# added on up and removed on down. The bridge a vif is added to can
-# be set in the vm config.
-#
-# The default bridge is xen-br0.
-# The default interface is eth0.
-#
-#============================================================================
-
-import sys
-import types
-
-from xen.util import Brctl
-
-from xen.xend import XendRoot
-xroot = XendRoot.instance()
-
-class VifControl:
-
- prefix = 'vifctl_'
-
- DEFAULT_BRIDGE = 'xen-br0'
- DEFAULT_INTERFACE = 'eth0'
-
- def __init__(self):
- self.name = 'vifctl'
-
- def main(self, args):
- #print self.name, args
- if len(args) < 2:
- usage(args)
- self.name = args[0]
- cmd = self.prefix + args[1]
- meth = getattr(self, cmd, self.unknown)
- meth(args[1:])
-
- def usage(self, args, out=sys.stderr):
- print >>out, 'Missing command, try \n%s help' % self.name
-
- def unknown(self, args, out=sys.stderr):
- print >>out, 'Unknown command:', args[1]
- self.help(out=out)
- sys.exit(1)
-
- def help(self, out=sys.stdout):
- print >>out, 'Commands are:',
- for x in vars(self):
- if x.startswith(prefix):
- cmd = x[len(prefix):]
- print >>out, cmd,
- print >>out
-
- def getparams(self, d, args, req=[]):
- """Parse args of the form 'key=val'. Valid keys are the ones
- in the dict 'd' passed in. If entries in 'd' have list values the
- values of the keys are appended.
-
- If 'req' is specified it is a list of required keys.
- """
- for x in args:
- (k, v) = x.split('=')
- k = k.strip()
- v = v.strip()
- if k not in d:
- print >>sys.stderr, 'Invalid parameter: ', k
- sys.exit(1)
- vold = d[k]
- if isinstance(vold , types.ListType):
- d[k] = vold + v
- else:
- d[k] = v
- for x in req:
- if not d[x]:
- print >>sys.stderr, 'Missing parameter:', x
- sys.exit(1)
- return d
-
- def vifctl_help(self, args):
- self.help()
-
- def default_bridge(self):
- return xroot.get_config_value('bridge', self.DEFAULT_BRIDGE)
-
- def default_interface(self):
- return xroot.get_config_value('interface', self.DEFAULT_INTERFACE)
-
- def vifctl_init(self, args):
- """Entry point for 'vifctl init'.
- """
- d = { 'bridge' : self.default_bridge(),
- 'interface': self.default_interface() }
- params = self.getparams(d, args[1:])
- interface = params['interface']
- bridge = params['bridge']
- # Create bridge 'bridge'.
- Brctl.bridge_create(bridge)
- # Reconfigure so that 'interface' is added to 'bridge',
- # and 'bridge' has the IP address from 'interface'.
- Brctl.reconfigure(interface, bridge)
-
- def vifparams(self, args):
- d = { 'vif' : None,
- 'mac' : None,
- 'bridge': self.default_bridge(),
- 'ipaddr': [] }
- d = self.getparams(d, args, req=['vif', 'mac'])
- return d
-
- def vifctl_up(self, args):
- """Entry point for 'vifctl up'.
- """
- params = self.vifparams(args[1:])
- # Add the vif to its bridge.
- Brctl.vif_bridge_add(params)
- if params['ipaddr']:
- # Add iptables rules for the ip addresses.
- vif = params['vif']
- for ipaddr in params['ipaddr']:
- Brctl.vif_restrict_addr(vif, ipaddr)
-
- def vifctl_down(self, args):
- """Entry point for 'vifctl down'.
- """
- params = self.vifparams(args[1:])
- # Remove the vif from its bridge.
- Brctl.vif_bridge_rem(params)
- if params['ipaddr']:
- # Remove iptables rules for the ip addresses.
- vif = params['vif']
- for ip in params['ipaddr']:
- Brctl.vif_restrict_addr(vif, ip, delete=1)
-
-
-if __name__ == "__main__":
- VifControl().main(sys.argv)
diff --git a/tools/examples/xend-config.sxp b/tools/examples/xend-config.sxp
index f710a54529..a62b112519 100644
--- a/tools/examples/xend-config.sxp
+++ b/tools/examples/xend-config.sxp
@@ -8,12 +8,17 @@
# Specifying the empty string '' allows all connections.
(xend-address '')
+## Use the following if VIF traffic is routed.
# The script used to start/stop networking for xend.
-(network-script network)
+#(network-script network-route)
+# The default script used to control virtual interfaces.
+#(vif-script vif-route)
+## Use the following if VIF traffic is bridged.
+# The script used to start/stop networking for xend.
+(network-script network)
# The default bridge that virtual interfaces should be connected to.
(vif-bridge xen-br0)
-
# The default script used to control virtual interfaces.
(vif-script vif-bridge)
@@ -21,3 +26,9 @@
# virtual interfaces. Specify 'yes' or 'no'.
(vif-antispoof no)
+# Setup script for file-backed block devices
+(block-file block-file)
+
+# Setup script for enbd-backed block devices
+(block-enbd block-enbd)
+
diff --git a/tools/examples/xmexample.vmx b/tools/examples/xmexample.vmx
new file mode 100644
index 0000000000..6e9039584b
--- /dev/null
+++ b/tools/examples/xmexample.vmx
@@ -0,0 +1,93 @@
+# -*- mode: python; -*-
+#============================================================================
+# Python configuration setup for 'xm create'.
+# This script sets the parameters used when a domain is created using 'xm create'.
+# You use a separate script for each domain you want to create, or
+# you can set the parameters for the domain on the xm command line.
+#============================================================================
+
+#----------------------------------------------------------------------------
+# Kernel image file.
+kernel = "/boot/vmlinuz-rhel3-static"
+
+# Optional ramdisk.
+#ramdisk = "/boot/initrd.gz"
+
+# The domain build function. Default is 'linux'.
+builder='vmx'
+#builder='linux'
+#builder='netbsd'
+
+# Initial memory allocation (in megabytes) for the new domain.
+memory = 128
+
+# A name for your domain. All domains must have different names.
+name = "ExampleVMXDomain"
+
+# Which CPU to start domain on?
+#cpu = -1 # leave to Xen to pick
+
+#----------------------------------------------------------------------------
+# Define network interfaces.
+
+# Number of network interfaces. Default is 1.
+#nics=1
+nics=0
+
+# Optionally define mac and/or bridge for the network interfaces.
+# Random MACs are assigned if not given.
+#vif = [ 'mac=aa:00:00:00:00:11, bridge=xen-br0' ]
+
+#----------------------------------------------------------------------------
+# Define the disk devices you want the domain to have access to, and
+# what you want them accessible as.
+# Each disk entry is of the form phy:UNAME,DEV,MODE
+# where UNAME is the device, DEV is the device name the domain will see,
+# and MODE is r for read-only, w for read-write.
+
+#disk = [ 'phy:hda1,hda1,r' ]
+
+#----------------------------------------------------------------------------
+# Set the kernel command line for the new domain.
+# You only need to define the IP parameters and hostname if the domain's
+# IP config doesn't, e.g. in ifcfg-eth0 or via DHCP.
+# You can use 'extra' to set the runlevel and custom environment
+# variables used by custom rc scripts (e.g. VMID=, usr= ).
+
+# Set if you want dhcp to allocate the IP address.
+#dhcp="dhcp"
+# Set netmask.
+#netmask=
+# Set default gateway.
+#gateway=
+# Set the hostname.
+#hostname= "vm%d" % vmid
+
+# Set root device.
+#root = "/dev/ram0"
+root = "/dev/hda1 ro"
+
+# Root device for nfs.
+#root = "/dev/nfs"
+# The nfs server.
+#nfs_server = '169.254.1.0'
+# Root directory on the nfs server.
+#nfs_root = '/full/path/to/root/directory'
+
+# Sets runlevel 4.
+extra = "1"
+
+#----------------------------------------------------------------------------
+# Set according to whether you want the domain restarted when it exits.
+# The default is 'onreboot', which restarts the domain when it shuts down
+# with exit code reboot.
+# Other values are 'always', and 'never'.
+
+#restart = 'onreboot'
+
+#============================================================================
+
+# New stuff
+memmap = '/etc/xen/mem-map.sxp'
+device_model = '/usr/sbin/device-model'
+device_config = '/etc/xen/bochsrc'
diff --git a/tools/examples/xmdefaults b/tools/examples/xmexample1
index 0b7c5b2230..b7b7dd437c 100644
--- a/tools/examples/xmdefaults
+++ b/tools/examples/xmexample1
@@ -1,6 +1,6 @@
# -*- mode: python; -*-
#============================================================================
-# Python defaults setup for 'xm create'.
+# Python configuration setup for 'xm create'.
# This script sets the parameters used when a domain is created using 'xm create'.
# You use a separate script for each domain you want to create, or
# you can set the parameters for the domain on the xm command line.
@@ -8,14 +8,13 @@
#----------------------------------------------------------------------------
# Kernel image file.
-kernel = "/boot/vmlinuz-2.4.26-xenU"
+kernel = "/boot/vmlinuz-2.6.9-xenU"
# Optional ramdisk.
#ramdisk = "/boot/initrd.gz"
# The domain build function. Default is 'linux'.
#builder='linux'
-#builder='netbsd'
# Initial memory allocation (in megabytes) for the new domain.
memory = 64
@@ -26,6 +25,9 @@ name = "ExampleDomain"
# Which CPU to start domain on?
#cpu = -1 # leave to Xen to pick
+# Number of Virtual CPUS to use, default is 1
+#vcpus = 1
+
#----------------------------------------------------------------------------
# Define network interfaces.
@@ -43,7 +45,7 @@ name = "ExampleDomain"
# where UNAME is the device, DEV is the device name the domain will see,
# and MODE is r for read-only, w for read-write.
-disk = [ 'phy:hda1,xda1,r' ]
+disk = [ 'phy:hda1,hda1,r' ]
#----------------------------------------------------------------------------
# Set the kernel command line for the new domain.
@@ -62,7 +64,7 @@ disk = [ 'phy:hda1,xda1,r' ]
#hostname= "vm%d" % vmid
# Set root device.
-root = "/dev/xda1 ro"
+root = "/dev/hda1 ro"
# Root device for nfs.
#root = "/dev/nfs"
diff --git a/tools/examples/xmexample b/tools/examples/xmexample2
index d6df731c45..07a80db366 100644
--- a/tools/examples/xmexample
+++ b/tools/examples/xmexample2
@@ -36,14 +36,13 @@ xm_vars.check()
#----------------------------------------------------------------------------
# Kernel image file.
-kernel = "/boot/vmlinuz-2.4.26-xenU"
+kernel = "/boot/vmlinuz-2.6.9-xenU"
# Optional ramdisk.
#ramdisk = "/boot/initrd.gz"
# The domain build function. Default is 'linux'.
#builder='linux'
-#builder='netbsd'
# Initial memory allocation (in megabytes) for the new domain.
memory = 64
@@ -56,6 +55,10 @@ name = "VM%d" % vmid
#cpu = -1 # leave to Xen to pick
cpu = vmid # set based on vmid (mod number of CPUs)
+# Number of Virtual CPUS to use, default is 1
+#vcpus = 1
+vcpus = 4 # make your domain a 4-way
+
#----------------------------------------------------------------------------
# Define network interfaces.
diff --git a/tools/examples/xmnetbsd-example b/tools/examples/xmexample3
index 8d1662483a..fd96a7b201 100644
--- a/tools/examples/xmnetbsd-example
+++ b/tools/examples/xmexample3
@@ -36,17 +36,24 @@ xm_vars.check()
#----------------------------------------------------------------------------
# Kernel image file.
-image = "/boot/netbsd"
+kernel = "/path/to/domU/kernel"
-# The domain build function.
-builder='netbsd'
+# Optional ramdisk.
+#ramdisk = "/boot/initrd.gz"
+
+# The domain build function. Default is 'linux'.
+#builder='linux'
# Initial memory allocation (in megabytes) for the new domain.
-memory = 16
+memory = 64
# A name for the new domain. All domains have to have different names,
# so we use the vmid to create a name.
-name = "NETBSD%d" % vmid
+name = "VM%d" % vmid
+
+# Which CPU to start domain on?
+#cpu = -1 # leave to Xen to pick
+cpu = vmid # set based on vmid (mod number of CPUs)
#----------------------------------------------------------------------------
# Define network interfaces.
@@ -56,16 +63,8 @@ name = "NETBSD%d" % vmid
# Optionally define mac and/or bridge for the network interfaces.
# Random MACs are assigned if not given.
-#vif = [ 'mac=aa:00:00:00:00:11, bridge=xen-br0' ]
-
-# Specify IP address(es), for the new domain. You need to
-# configure IP addrs within the domain just as you do normally. This
-# is just to let Xen know about them so it can route packets
-# appropriately.
-#ipaddr = [ xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid),
-# xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),
-# ]
+vif = [ 'ip=192.168.%d.1/24' % (vmid)]
#----------------------------------------------------------------------------
# Define the disk devices you want the domain to have access to, and
@@ -75,11 +74,10 @@ name = "NETBSD%d" % vmid
# and MODE is r for read-only, w for read-write.
# This makes the disk device depend on the vmid - assuming
-# that devices sda7, sda8 etc. exist. The device is exported
+# tHat devices sda7, sda8 etc. exist. The device is exported
# to all domains as sda1.
# All domains get sda6 read-only (to use for /usr, see below).
-disk = [ 'phy:sda%d,sda1,w' % (7+vmid),
- 'phy:sda6,sda6,r' ]
+disk = [ 'phy:hda%d,hda1,w' % (vmid)]
#----------------------------------------------------------------------------
# Set the kernel command line for the new domain.
@@ -89,35 +87,34 @@ disk = [ 'phy:sda%d,sda1,w' % (7+vmid),
# variables used by custom rc scripts (e.g. VMID=, usr= ).
# Set if you want dhcp to allocate the IP address.
-#dhcp="dhcp"
+dhcp="off"
+ip="192.168.%d.2" % (vmid)
# Set netmask.
-#netmask=
+netmask="255.255.255.0"
# Set default gateway.
-#gateway=
+gateway="192.168.%d.1" % (vmid)
# Set the hostname.
-#hostname= "vm%d" % vmid
+hostname= "domain-%d.xeno" % vmid
# Set root device.
-root = "/dev/sda1 ro"
+root = "/dev/hda1 ro"
# Root device for nfs.
#root = "/dev/nfs"
# The nfs server.
-#nfs_server = '169.254.1.0'
+#nfs_server = "10.212.4.103"
# Root directory on the nfs server.
-#nfs_root = '/full/path/to/root/directory'
+#nfs_root = "/path/to/root/filesystem"
# Sets runlevel 4 and the device for /usr.
-#extra = "4 VMID=%d usr=/dev/sda6" % vmid
-extra = "4 VMID=%d bootdev=xennet0" % vmid
-
+extra = "4 VMID=%d" % vmid
#----------------------------------------------------------------------------
# Set according to whether you want the domain restarted when it exits.
# The default is 'onreboot', which restarts the domain when it shuts down
# with exit code reboot.
# Other values are 'always', and 'never'.
-#
+
#restart = 'onreboot'
#============================================================================
diff --git a/tools/ioemu/Makefile b/tools/ioemu/Makefile
new file mode 100644
index 0000000000..24215c5a6f
--- /dev/null
+++ b/tools/ioemu/Makefile
@@ -0,0 +1,19 @@
+# Order is important!
+SUBDIRS=gui memory iodev
+
+.PHONY: all clean install
+
+all:
+ @for subdir in $(SUBDIRS); do \
+ $(MAKE) -C $$subdir $(MAKEDEFS) $@ || exit -1; \
+ done
+
+clean:
+ @for subdir in $(SUBDIRS); do \
+ $(MAKE) -C $$subdir $(MAKEDEFS) $@ || exit -1; \
+ done
+
+install:
+ @for subdir in $(SUBDIRS); do \
+ $(MAKE) -C $$subdir $(MAKEDEFS) $@ || exit -1; \
+ done
diff --git a/tools/ioemu/font/vga.bitmap.h b/tools/ioemu/font/vga.bitmap.h
new file mode 100644
index 0000000000..e82dd629e6
--- /dev/null
+++ b/tools/ioemu/font/vga.bitmap.h
@@ -0,0 +1,288 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: vga.bitmap.h,v 1.4 2002/05/25 14:22:53 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+typedef struct {
+ unsigned char data[16];
+ } bx_fontcharbitmap_t;
+
+static const bx_fontcharbitmap_t bx_vgafont[256] = {
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xa5, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xdb, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x36, 0x7f, 0x7f, 0x7f, 0x7f, 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x3e, 0x7f, 0x3e, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff }},
+{{ 0x00, 0x00, 0x78, 0x60, 0x70, 0x58, 0x1e, 0x33, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0xfc, 0xcc, 0xfc, 0x0c, 0x0c, 0x0c, 0x0c, 0x0e, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0xfe, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xe6, 0xe7, 0x67, 0x03, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x40, 0x60, 0x70, 0x78, 0x7c, 0x7f, 0x7c, 0x78, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0xfe, 0xdb, 0xdb, 0xdb, 0xde, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x3e, 0x63, 0x06, 0x1c, 0x36, 0x63, 0x63, 0x36, 0x1c, 0x30, 0x63, 0x3e, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x7f, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x06, 0x7f, 0x06, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x36, 0x7f, 0x36, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x1c, 0x3e, 0x3e, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x3e, 0x3e, 0x1c, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x36, 0x7f, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x18, 0x18, 0x3e, 0x63, 0x43, 0x03, 0x3e, 0x60, 0x60, 0x61, 0x63, 0x3e, 0x18, 0x18, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x43, 0x63, 0x30, 0x18, 0x0c, 0x06, 0x63, 0x61, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1c, 0x36, 0x36, 0x1c, 0x6e, 0x3b, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x0c, 0x0c, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x6b, 0x6b, 0x63, 0x63, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x1c, 0x1e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x60, 0x60, 0x3c, 0x60, 0x60, 0x60, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x30, 0x38, 0x3c, 0x36, 0x33, 0x7f, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7f, 0x03, 0x03, 0x03, 0x3f, 0x60, 0x60, 0x60, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1c, 0x06, 0x03, 0x03, 0x3f, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7f, 0x63, 0x60, 0x60, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x60, 0x60, 0x30, 0x1e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x63, 0x30, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x7b, 0x7b, 0x7b, 0x3b, 0x03, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x03, 0x03, 0x43, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1f, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x36, 0x1f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x46, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7f, 0x66, 0x46, 0x16, 0x1e, 0x16, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x7b, 0x63, 0x63, 0x66, 0x5c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x33, 0x33, 0x1e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x67, 0x66, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x77, 0x7f, 0x7f, 0x6b, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x7b, 0x3e, 0x30, 0x70, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3f, 0x66, 0x66, 0x66, 0x3e, 0x36, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3e, 0x63, 0x63, 0x06, 0x1c, 0x30, 0x60, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x36, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x6b, 0x6b, 0x6b, 0x7f, 0x77, 0x36, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x63, 0x36, 0x3e, 0x1c, 0x1c, 0x3e, 0x36, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7f, 0x63, 0x61, 0x30, 0x18, 0x0c, 0x06, 0x43, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0x60, 0x40, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x08, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00 }},
+{{ 0x0c, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x07, 0x06, 0x06, 0x1e, 0x36, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x03, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x38, 0x30, 0x30, 0x3c, 0x36, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x30, 0x33, 0x1e, 0x00 }},
+{{ 0x00, 0x00, 0x07, 0x06, 0x06, 0x36, 0x6e, 0x66, 0x66, 0x66, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x60, 0x60, 0x00, 0x70, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x66, 0x66, 0x3c, 0x00 }},
+{{ 0x00, 0x00, 0x07, 0x06, 0x06, 0x66, 0x36, 0x1e, 0x1e, 0x36, 0x66, 0x67, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x7f, 0x6b, 0x6b, 0x6b, 0x6b, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x0f, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3e, 0x30, 0x30, 0x78, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x6e, 0x66, 0x06, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x63, 0x06, 0x1c, 0x30, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x08, 0x0c, 0x0c, 0x3f, 0x0c, 0x0c, 0x0c, 0x0c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x6b, 0x6b, 0x6b, 0x7f, 0x36, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x36, 0x1c, 0x1c, 0x1c, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1f, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x33, 0x18, 0x0c, 0x06, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x3c, 0x66, 0x43, 0x03, 0x03, 0x03, 0x43, 0x66, 0x3c, 0x30, 0x60, 0x3e, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x33, 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x30, 0x18, 0x0c, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x08, 0x1c, 0x36, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x33, 0x00, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x06, 0x0c, 0x18, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x1c, 0x36, 0x1c, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x66, 0x3c, 0x30, 0x60, 0x3c, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x00, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x06, 0x0c, 0x18, 0x00, 0x3e, 0x63, 0x7f, 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x66, 0x00, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x06, 0x0c, 0x18, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x63, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x1c, 0x36, 0x1c, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x18, 0x0c, 0x06, 0x00, 0x7f, 0x66, 0x06, 0x3e, 0x06, 0x06, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x6e, 0x6c, 0x7e, 0x1b, 0x1b, 0x76, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7c, 0x36, 0x33, 0x33, 0x7f, 0x33, 0x33, 0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x08, 0x1c, 0x36, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x06, 0x0c, 0x18, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x0c, 0x1e, 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x06, 0x0c, 0x18, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x63, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x7e, 0x60, 0x30, 0x1e, 0x00 }},
+{{ 0x00, 0x63, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x63, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x18, 0x18, 0x3c, 0x66, 0x06, 0x06, 0x06, 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x1c, 0x36, 0x26, 0x06, 0x0f, 0x06, 0x06, 0x06, 0x06, 0x67, 0x3f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x1f, 0x33, 0x33, 0x1f, 0x23, 0x33, 0x7b, 0x33, 0x33, 0x33, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x70, 0xd8, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x0e, 0x00, 0x00 }},
+{{ 0x00, 0x18, 0x0c, 0x06, 0x00, 0x1e, 0x30, 0x3e, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x30, 0x18, 0x0c, 0x00, 0x1c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x18, 0x0c, 0x06, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x18, 0x0c, 0x06, 0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x6e, 0x3b, 0x00, 0x3b, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x6e, 0x3b, 0x00, 0x63, 0x67, 0x6f, 0x7f, 0x7b, 0x73, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x3c, 0x36, 0x36, 0x7c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x1c, 0x36, 0x36, 0x1c, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x0c, 0x0c, 0x06, 0x03, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, 0x06, 0x3b, 0x61, 0x30, 0x18, 0x7c, 0x00, 0x00 }},
+{{ 0x00, 0x03, 0x03, 0x43, 0x63, 0x33, 0x18, 0x0c, 0x66, 0x73, 0x79, 0x7c, 0x60, 0x60, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x36, 0x1b, 0x36, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x36, 0x6c, 0x36, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22 }},
+{{ 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }},
+{{ 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee, 0xbb, 0xee }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x60, 0x6f, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6f, 0x60, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xec, 0x0c, 0xec, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xef, 0x00, 0xef, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0xff, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }},
+{{ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f }},
+{{ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 }},
+{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x1b, 0x1b, 0x3b, 0x6e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1e, 0x33, 0x33, 0x33, 0x1b, 0x33, 0x63, 0x63, 0x63, 0x33, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x7f, 0x63, 0x63, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x7f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x7f, 0x63, 0x06, 0x0c, 0x18, 0x0c, 0x06, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3e, 0x06, 0x06, 0x03, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x7f, 0x63, 0x63, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x63, 0x36, 0x36, 0x36, 0x36, 0x77, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x78, 0x0c, 0x18, 0x30, 0x7c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0xc0, 0x60, 0x7e, 0xdb, 0xdb, 0xcf, 0x7e, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x38, 0x0c, 0x06, 0x06, 0x3e, 0x06, 0x06, 0x06, 0x0c, 0x38, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x70, 0xd8, 0xd8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 }},
+{{ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1b, 0x1b, 0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x00, 0x6e, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x1c, 0x36, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x37, 0x36, 0x36, 0x3c, 0x38, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x1b, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x0e, 0x1b, 0x0c, 0x06, 0x13, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+{{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
+};
diff --git a/tools/ioemu/gui/Makefile b/tools/ioemu/gui/Makefile
new file mode 100644
index 0000000000..52ce67bd20
--- /dev/null
+++ b/tools/ioemu/gui/Makefile
@@ -0,0 +1,12 @@
+TOPDIR= ..
+CXXFLAGS=-I. -I../include -I..
+OBJS= gui.o keymap.o siminterface.o textconfig.o x.o
+
+all: libgui.a
+
+libgui.a: $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+
+include $(TOPDIR)/mk/helix.mk
+
+install:: all
diff --git a/tools/ioemu/gui/Makefile.in b/tools/ioemu/gui/Makefile.in
new file mode 100644
index 0000000000..c9fd86558a
--- /dev/null
+++ b/tools/ioemu/gui/Makefile.in
@@ -0,0 +1,561 @@
+# Copyright (C) 2002 MandrakeSoft S.A.
+#
+# MandrakeSoft S.A.
+# 43, rue d'Aboukir
+# 75002 Paris - France
+# http://www.linux-mandrake.com/
+# http://www.mandrakesoft.com/
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# Makefile for the gui component of bochs
+
+
+@SUFFIX_LINE@
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+srcdir = @srcdir@
+VPATH = @srcdir@
+bindir = @bindir@
+libdir = @libdir@
+mandir = @mandir@
+man1dir = $(mandir)/man1
+man5dir = $(mandir)/man5
+docdir = $(prefix)/share/doc/bochs
+sharedir = $(prefix)/share/bochs
+top_builddir = ..
+top_srcdir = @top_srcdir@
+
+SHELL = /bin/sh
+
+@SET_MAKE@
+
+CXX = @CXX@
+CXXFLAGS = $(BX_INCDIRS) @CXXFLAGS@ @GUI_CXXFLAGS@
+LOCAL_CXXFLAGS =
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+X_CFLAGS = @X_CFLAGS@
+RANLIB = @RANLIB@
+PLUGIN_PATH=@libdir@
+top_builddir = ..
+LIBTOOL=@LIBTOOL@
+WIN32_DLL_IMPORT_LIBRARY=../dllexports.a
+BX_INCDIRS = -I.. -I$(srcdir)/.. -I../iodev -I$(srcdir)/../iodev -I../@INSTRUMENT_DIR@ -I$(srcdir)/../@INSTRUMENT_DIR@
+
+GUI_OBJS_X11 = x.o
+GUI_OBJS_SDL = sdl.o
+GUI_OBJS_SVGA = svga.o
+GUI_OBJS_BEOS = beos.o
+GUI_OBJS_WIN32 = win32.o
+GUI_OBJS_MACOS = macintosh.o
+GUI_OBJS_CARBON = carbon.o
+GUI_OBJS_NOGUI = nogui.o
+GUI_OBJS_TERM = term.o
+GUI_OBJS_RFB = rfb.o
+GUI_OBJS_AMIGAOS = amigaos.o
+GUI_OBJS_WX = wx.o
+GUI_OBJS_WX_SUPPORT = wxmain.o wxdialog.o
+OBJS_THAT_CANNOT_BE_PLUGINS = keymap.o gui.o siminterface.o textconfig.o @DIALOG_OBJS@
+OBJS_THAT_CAN_BE_PLUGINS = @GUI_OBJS@
+
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+GUI_LINK_OPTS_X = $(X_LIBS) $(X_PRE_LIBS) -lX11 -lXpm
+GUI_LINK_OPTS_SDL = `sdl-config --cflags --libs`
+GUI_LINK_OPTS_SVGA = -lvga -lvgagl
+GUI_LINK_OPTS_BEOS = -lbe
+GUI_LINK_OPTS_RFB = @RFB_LIBS@
+GUI_LINK_OPTS_AMIGAOS =
+GUI_LINK_OPTS_WIN32 = -luser32 -lgdi32 -lcomdlg32 -lcomctl32
+GUI_LINK_OPTS_WIN32_VCPP = user32.lib gdi32.lib winmm.lib \
+ comdlg32.lib comctl32.lib wsock32.lib
+GUI_LINK_OPTS_MACOS =
+GUI_LINK_OPTS_CARBON = -framework Carbon
+GUI_LINK_OPTS_NOGUI =
+GUI_LINK_OPTS_TERM = @GUI_LINK_OPTS_TERM@
+GUI_LINK_OPTS_WX = @GUI_LINK_OPTS_WX@
+GUI_LINK_OPTS = @GUI_LINK_OPTS@ @DEVICE_LINK_OPTS@
+
+NONPLUGIN_OBJS = @GUI_NON_PLUGIN_OBJS@
+PLUGIN_OBJS = @GUI_PLUGIN_OBJS@
+
+#
+# -------- end configurable options --------------------------
+#
+
+all: libgui.a
+
+plugins: $(PLUGIN_OBJS:@PLUGIN_LIBNAME_TRANSFORMATION@)
+
+libgui.a: $(NONPLUGIN_OBJS)
+ @RMCOMMAND@ libgui.a
+ @MAKELIB@ $(NONPLUGIN_OBJS)
+ @RANLIB@ libgui.a
+
+# standard compile rule for C++ files
+.@CPP_SUFFIX@.o:
+ $(CXX) @DASH@c $(CXXFLAGS) $(LOCAL_CXXFLAGS) @CXXFP@$< @OFP@$@
+
+##### building plugins with libtool
+%.lo: %.@CPP_SUFFIX@
+ $(LIBTOOL) --mode=compile $(CXX) -c $(CXXFLAGS) $(LOCAL_CXXFLAGS) $< -o $@
+
+libbx_%.la: %.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH)
+
+libbx_x.la: x.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_X)
+
+libbx_sdl.la: sdl.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_SDL)
+
+libbx_svga.la: svga.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_SVGA)
+
+libbx_beos.la: beos.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_BEOS)
+
+libbx_rfb.la: rfb.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_RFB)
+
+libbx_amigaos.la: amigaos.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_AMIGAOS)
+
+libbx_win32.la: win32.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_WIN32)
+
+libbx_macos.la: macos.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_MACOS)
+
+libbx_carbon.la: carbon.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_CARBON)
+
+libbx_nogui.la: nogui.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_NOGUI)
+
+libbx_term.la: term.lo
+ $(LIBTOOL) --mode=link $(CXX) -module $< -o $@ -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_TERM)
+
+# special link rules for plugins that require more than one object file
+libbx_wx.la: $(GUI_OBJS_WX:.o=.lo) $(GUI_OBJS_WX_SUPPORT:.o=.lo)
+ $(LIBTOOL) --mode=link $(CXX) -module $(GUI_OBJS_WX:.o=.lo) $(GUI_OBJS_WX_SUPPORT:.o=.lo) -o libbx_wx.la -rpath $(PLUGIN_PATH) $(GUI_LINK_OPTS_WX)
+
+#### building DLLs for win32 (tested on cygwin only)
+bx_%.dll: %.o
+ $(CXX) $(CXXFLAGS) -shared -o $@ $< $(WIN32_DLL_IMPORT_LIBRARY) $(GUI_LINK_OPTS_WIN32)
+
+bx_wx.dll: $(GUI_OBJS_WX) $(GUI_OBJS_WX_SUPPORT)
+ $(CXX) $(CXXFLAGS) -shared -o bx_wx.dll $(GUI_OBJS_WX) $(GUI_OBJS_WX_SUPPORT) $(WIN32_DLL_IMPORT_LIBRARY) `wx-config --libs` -luser32 -lgdi32 -lcomdlg32 -lcomctl32
+
+bx_sdl.dll: $(GUI_OBJS_SDL)
+ $(CXX) $(CXXFLAGS) -shared -o bx_sdl.dll $(GUI_OBJS_SDL) $(WIN32_DLL_IMPORT_LIBRARY) $(GUI_LINK_OPTS_SDL)
+
+bx_rfb.dll: $(GUI_OBJS_RFB)
+ $(CXX) $(CXXFLAGS) -shared -o bx_rfb.dll $(GUI_OBJS_RFB) $(WIN32_DLL_IMPORT_LIBRARY) $(GUI_LINK_OPTS_RFB)
+
+# no need to build DLLs for beos.o
+# no need to build DLLs for x.o
+
+##### end DLL section
+
+clean:
+ @RMCOMMAND@ -rf .libs *.la *.a *.lo *.o *.dll
+
+dist-clean: clean
+ @RMCOMMAND@ Makefile
+
+###########################################
+# all other dependencies generated by
+# gcc -MM -I.. -I../instrument/stubs `wx-config --cxxflags` *.cc |
+# sed -e 's/\.cc/.@CPP_SUFFIX@/g'
+# gcc -MM -I.. -I../instrument/stubs `wx-config --cxxflags` *.cc | \
+# sed -e 's/\.cc/.@CPP_SUFFIX@/g' -e 's/\.o:/.lo:/g'
+#
+# This means that every source file is listed twice, once with a .o rule
+# and then again with an identical .lo rule. The .lo rules are used when
+# building plugins.
+###########################################
+amigaos.o: amigaos.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h \
+ icon_bochs.h amigagui.h
+beos.o: beos.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h ../font/vga.bitmap.h
+carbon.o: carbon.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h ../font/vga.bitmap.h
+gui.o: gui.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h ../gui/bitmaps/floppya.h \
+ ../gui/bitmaps/floppyb.h ../gui/bitmaps/mouse.h \
+ ../gui/bitmaps/reset.h ../gui/bitmaps/power.h \
+ ../gui/bitmaps/snapshot.h ../gui/bitmaps/copy.h \
+ ../gui/bitmaps/paste.h ../gui/bitmaps/configbutton.h \
+ ../gui/bitmaps/cdromd.h ../gui/bitmaps/userbutton.h
+keymap.o: keymap.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h
+macintosh.o: macintosh.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h \
+ icon_bochs.h ../font/vga.bitmap.h
+nogui.o: nogui.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h
+rfb.o: rfb.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h ../font/vga.bitmap.h \
+ rfbproto.h
+sdl.o: sdl.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h sdl.h sdlkeys.h
+siminterface.o: siminterface.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h
+svga.o: svga.@CPP_SUFFIX@ ../font/vga.bitmap.h ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h \
+ icon_bochs.h
+term.o: term.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h
+textconfig.o: textconfig.@CPP_SUFFIX@ ../config.h ../osdep.h textconfig.h \
+ siminterface.h ../extplugin.h ../ltdl.h
+win32.o: win32.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h ../font/vga.bitmap.h
+wx.o: wx.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h ../gui/icon_bochs.h \
+ ../font/vga.bitmap.h wxmain.h
+wxdialog.o: wxdialog.@CPP_SUFFIX@ ../config.h ../osdep.h ../gui/siminterface.h \
+ ../bxversion.h wxdialog.h wxmain.h
+wxmain.o: wxmain.@CPP_SUFFIX@ ../config.h ../osdep.h ../gui/siminterface.h \
+ ../bxversion.h wxdialog.h wxmain.h ../extplugin.h ../ltdl.h \
+ bitmaps/cdromd.xpm bitmaps/copy.xpm bitmaps/floppya.xpm \
+ bitmaps/floppyb.xpm bitmaps/paste.xpm bitmaps/power.xpm \
+ bitmaps/reset.xpm bitmaps/snapshot.xpm bitmaps/mouse.xpm \
+ bitmaps/userbutton.xpm
+x.o: x.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h
+amigaos.lo: amigaos.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h \
+ icon_bochs.h amigagui.h
+beos.lo: beos.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h ../font/vga.bitmap.h
+carbon.lo: carbon.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h ../font/vga.bitmap.h
+gui.lo: gui.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h ../gui/bitmaps/floppya.h \
+ ../gui/bitmaps/floppyb.h ../gui/bitmaps/mouse.h \
+ ../gui/bitmaps/reset.h ../gui/bitmaps/power.h \
+ ../gui/bitmaps/snapshot.h ../gui/bitmaps/copy.h \
+ ../gui/bitmaps/paste.h ../gui/bitmaps/configbutton.h \
+ ../gui/bitmaps/cdromd.h ../gui/bitmaps/userbutton.h
+keymap.lo: keymap.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h
+macintosh.lo: macintosh.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h \
+ icon_bochs.h ../font/vga.bitmap.h
+nogui.lo: nogui.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h
+rfb.lo: rfb.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h ../font/vga.bitmap.h \
+ rfbproto.h
+sdl.lo: sdl.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h sdl.h sdlkeys.h
+siminterface.lo: siminterface.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h
+svga.lo: svga.@CPP_SUFFIX@ ../font/vga.bitmap.h ../bochs.h ../config.h ../osdep.h \
+ ../bx_debug/debug.h ../bxversion.h ../gui/siminterface.h ../state_file.h \
+ ../cpu/cpu.h ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h \
+ ../pc_system.h ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h \
+ ../gui/textconfig.h ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h \
+ ../iodev/biosdev.h ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h \
+ ../iodev/harddrv.h ../iodev/cdrom.h ../iodev/keyboard.h \
+ ../iodev/parallel.h ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h \
+ ../iodev/pit82c54.h ../iodev/serial.h ../iodev/unmapped.h \
+ ../iodev/eth.h ../iodev/ne2k.h ../iodev/guest2host.h \
+ ../iodev/slowdown_timer.h ../instrument/stubs/instrument.h \
+ icon_bochs.h
+term.lo: term.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h
+textconfig.lo: textconfig.@CPP_SUFFIX@ ../config.h ../osdep.h textconfig.h \
+ siminterface.h ../extplugin.h ../ltdl.h
+win32.lo: win32.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h ../font/vga.bitmap.h
+wx.lo: wx.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h ../gui/icon_bochs.h \
+ ../font/vga.bitmap.h wxdialog.h wxmain.h
+wxdialog.lo: wxdialog.@CPP_SUFFIX@ ../config.h ../osdep.h ../gui/siminterface.h \
+ ../bxversion.h wxdialog.h wxmain.h
+wxmain.lo: wxmain.@CPP_SUFFIX@ ../config.h ../osdep.h ../gui/siminterface.h \
+ ../bxversion.h wxdialog.h wxmain.h ../extplugin.h ../ltdl.h \
+ bitmaps/cdromd.xpm bitmaps/copy.xpm bitmaps/floppya.xpm \
+ bitmaps/floppyb.xpm bitmaps/paste.xpm bitmaps/power.xpm \
+ bitmaps/reset.xpm bitmaps/snapshot.xpm bitmaps/mouse.xpm \
+ bitmaps/userbutton.xpm
+x.lo: x.@CPP_SUFFIX@ ../bochs.h ../config.h ../osdep.h ../bx_debug/debug.h \
+ ../bxversion.h ../gui/siminterface.h ../state_file.h ../cpu/cpu.h \
+ ../cpu/lazy_flags.h ../cpu/i387.h ../memory/memory.h ../pc_system.h \
+ ../plugin.h ../extplugin.h ../ltdl.h ../gui/gui.h ../gui/textconfig.h \
+ ../gui/keymap.h ../iodev/iodev.h ../iodev/vga.h ../iodev/biosdev.h \
+ ../iodev/cmos.h ../iodev/dma.h ../iodev/floppy.h ../iodev/harddrv.h \
+ ../iodev/cdrom.h ../iodev/keyboard.h ../iodev/parallel.h \
+ ../iodev/pic.h ../iodev/pit.h ../iodev/pit_wrap.h ../iodev/pit82c54.h \
+ ../iodev/serial.h ../iodev/unmapped.h ../iodev/eth.h ../iodev/ne2k.h \
+ ../iodev/guest2host.h ../iodev/slowdown_timer.h \
+ ../instrument/stubs/instrument.h icon_bochs.h
diff --git a/tools/ioemu/gui/bitmaps/cdromd.h b/tools/ioemu/gui/bitmaps/cdromd.h
new file mode 100644
index 0000000000..49d78e122c
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/cdromd.h
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: cdromd.h,v 1.1 2002/01/31 21:16:52 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#define BX_CDROMD_BMAP_X 32
+#define BX_CDROMD_BMAP_Y 32
+
+static const unsigned char bx_cdromd_bmap[(BX_CONFIG_BMAP_X * BX_CONFIG_BMAP_Y)/8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x0e, 0x00, 0x00, 0x10, 0x12, 0x00,
+ 0x00, 0x10, 0x12, 0x00, 0x00, 0x10, 0x12, 0x00, 0x00, 0x60, 0x0e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x0c, 0x30, 0x00,
+ 0x00, 0xe2, 0x47, 0x00, 0x00, 0x19, 0x98, 0x00, 0x80, 0xe6, 0x67, 0x01,
+ 0x40, 0x19, 0x98, 0x02, 0x20, 0xe5, 0xa7, 0x04, 0xa0, 0x12, 0x48, 0x05,
+ 0x90, 0xca, 0x53, 0x09, 0x50, 0x25, 0xa4, 0x0a, 0x50, 0x15, 0xa8, 0x0a,
+ 0x50, 0x15, 0xa8, 0x0a, 0x50, 0x15, 0xa8, 0x0a, 0x50, 0x15, 0xa8, 0x0a,
+ 0x50, 0x25, 0xa4, 0x0a, 0x90, 0xca, 0x53, 0x09, 0xa0, 0x12, 0x48, 0x05,
+ 0x20, 0xe5, 0xa7, 0x04, 0x40, 0x19, 0x98, 0x02, 0x80, 0xe6, 0x67, 0x01,
+ 0x00, 0x19, 0x98, 0x00, 0x00, 0xe2, 0x47, 0x00, 0x00, 0x0c, 0x30, 0x00,
+ 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+static const unsigned char bx_cdromd_eject_bmap[(BX_CONFIG_BMAP_X * BX_CONFIG_BMAP_Y)/8] = {
+ 0x01, 0x00, 0x00, 0x80, 0x02, 0x60, 0x0e, 0x40, 0x04, 0x10, 0x12, 0x20,
+ 0x08, 0x10, 0x12, 0x10, 0x10, 0x10, 0x12, 0x08, 0x20, 0x60, 0x0e, 0x04,
+ 0x40, 0x00, 0x00, 0x02, 0x80, 0xf0, 0x0f, 0x01, 0x00, 0x0d, 0xb0, 0x00,
+ 0x00, 0xe2, 0x47, 0x00, 0x00, 0x1d, 0xb8, 0x00, 0x80, 0xee, 0x77, 0x01,
+ 0x40, 0x19, 0x98, 0x02, 0x20, 0xe5, 0xa7, 0x04, 0xa0, 0x52, 0x4a, 0x05,
+ 0x90, 0xca, 0x53, 0x09, 0x50, 0xa5, 0xa5, 0x0a, 0x50, 0x55, 0xaa, 0x0a,
+ 0x50, 0x35, 0xac, 0x0a, 0x50, 0x15, 0xa8, 0x0a, 0x50, 0x1d, 0xb8, 0x0a,
+ 0x50, 0x25, 0xa4, 0x0a, 0x90, 0xca, 0x53, 0x09, 0xa0, 0x13, 0xc8, 0x05,
+ 0xa0, 0xe5, 0xa7, 0x05, 0x40, 0x19, 0x98, 0x02, 0xa0, 0xe6, 0x67, 0x05,
+ 0x10, 0x19, 0x98, 0x08, 0x08, 0xe2, 0x47, 0x10, 0x04, 0x0c, 0x30, 0x20,
+ 0x02, 0xf0, 0x0f, 0x40, 0x01, 0x00, 0x00, 0x80
+ };
diff --git a/tools/ioemu/gui/bitmaps/cdromd.xpm b/tools/ioemu/gui/bitmaps/cdromd.xpm
new file mode 100644
index 0000000000..7a216b0bfe
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/cdromd.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *cdromd_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+".............##..###............",
+"............#....#..#...........",
+"............#....#..#...........",
+"............#....#..#...........",
+".............##..###............",
+"................................",
+"............########............",
+"..........##........##..........",
+".........#...######...#.........",
+"........#..##......##..#........",
+".......#.##..######..##.#.......",
+"......#.#..##......##..#.#......",
+".....#..#.#..######..#.#..#.....",
+".....#.#.#..#......#..#.#.#.....",
+"....#..#.#.#..####..#.#.#..#....",
+"....#.#.#.#..#....#..#.#.#.#....",
+"....#.#.#.#.#......#.#.#.#.#....",
+"....#.#.#.#.#......#.#.#.#.#....",
+"....#.#.#.#.#......#.#.#.#.#....",
+"....#.#.#.#.#......#.#.#.#.#....",
+"....#.#.#.#..#....#..#.#.#.#....",
+"....#..#.#.#..####..#.#.#..#....",
+".....#.#.#..#......#..#.#.#.....",
+".....#..#.#..######..#.#..#.....",
+"......#.#..##......##..#.#......",
+".......#.##..######..##.#.......",
+"........#..##......##..#........",
+".........#...######...#.........",
+"..........##........##..........",
+"............########............",
+"................................"
+};
diff --git a/tools/ioemu/gui/bitmaps/configbutton.h b/tools/ioemu/gui/bitmaps/configbutton.h
new file mode 100644
index 0000000000..7c51ff9b69
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/configbutton.h
@@ -0,0 +1,16 @@
+#define BX_CONFIG_BMAP_X 32
+#define BX_CONFIG_BMAP_Y 32
+
+static const unsigned char bx_config_bmap[(BX_CONFIG_BMAP_X * BX_CONFIG_BMAP_Y)/8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x80, 0x01, 0xfc, 0x7f, 0xc0, 0x03,
+ 0xfc, 0xff, 0xc1, 0x03, 0xfc, 0xff, 0xc1, 0x03, 0xfc, 0x7f, 0xc0, 0x03,
+ 0x84, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03,
+ 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03,
+ 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03,
+ 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xc0, 0x03,
+ 0x80, 0x07, 0xc0, 0x03, 0x80, 0x07, 0xe0, 0x07, 0x80, 0x07, 0xf0, 0x0f,
+ 0x80, 0x07, 0x70, 0x0e, 0x80, 0x07, 0x30, 0x0c, 0x80, 0x07, 0x30, 0x0c,
+ 0x80, 0x07, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x38, 0x27, 0xba, 0x1c,
+ 0x84, 0x68, 0x8a, 0x02, 0x84, 0xa8, 0xba, 0x32, 0x84, 0x28, 0x8b, 0x22,
+ 0x38, 0x27, 0x8a, 0x1c, 0x00, 0x00, 0x00, 0x00
+ };
diff --git a/tools/ioemu/gui/bitmaps/configbutton.xpm b/tools/ioemu/gui/bitmaps/configbutton.xpm
new file mode 100644
index 0000000000..ef2f933828
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/configbutton.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *configbutton_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+"..#....................##.......",
+"..#############.......####......",
+"..###############.....####......",
+"..###############.....####......",
+"..#############.......####......",
+"..#....####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####...........####......",
+".......####..........######.....",
+".......####.........########....",
+".......####.........###..###....",
+".......####.........##....##....",
+".......####.........##....##....",
+".......####..........#....#.....",
+"................................",
+"...###..###..#...#.###.#..###...",
+"..#....#...#.##..#.#...#.#......",
+"..#....#...#.#.#.#.###.#.#..##..",
+"..#....#...#.#..##.#...#.#...#..",
+"...###..###..#...#.#...#..###...",
+"................................"
+};
diff --git a/tools/ioemu/gui/bitmaps/copy.h b/tools/ioemu/gui/bitmaps/copy.h
new file mode 100644
index 0000000000..78546c979f
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/copy.h
@@ -0,0 +1,18 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: copy.h,v 1.1 2002/03/11 15:04:58 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+#define BX_COPY_BMAP_X 32
+#define BX_COPY_BMAP_Y 32
+
+static unsigned char bx_copy_bmap[(BX_COPY_BMAP_X*BX_COPY_BMAP_Y)] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x80, 0x60, 0x4e, 0x02, 0x80, 0x90, 0x52, 0x02, 0x80, 0x90, 0x52, 0x02,
+ 0x00, 0x6f, 0x8e, 0x03, 0x00, 0x00, 0x02, 0x02, 0xf8, 0x3f, 0x02, 0x02,
+ 0x08, 0x20, 0xc0, 0x01, 0xe8, 0x2b, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00,
+ 0xe8, 0x2e, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0xe8, 0x39, 0x00, 0x00,
+ 0x08, 0x24, 0x00, 0x00, 0x88, 0x20, 0x00, 0x00, 0xe8, 0xaf, 0xff, 0x03,
+ 0x08, 0xa0, 0x00, 0x02, 0xf8, 0xbf, 0xbe, 0x02, 0x00, 0x80, 0x00, 0x02,
+ 0x80, 0x88, 0xee, 0x02, 0x80, 0x90, 0x00, 0x02, 0x00, 0xbf, 0x9e, 0x03,
+ 0x00, 0x90, 0x40, 0x02, 0x00, 0x88, 0x08, 0x02, 0x00, 0x80, 0xfe, 0x02,
+ 0x00, 0x80, 0x00, 0x02, 0x00, 0x80, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
diff --git a/tools/ioemu/gui/bitmaps/copy.xpm b/tools/ioemu/gui/bitmaps/copy.xpm
new file mode 100644
index 0000000000..5e0138e87a
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/copy.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *copy_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+"........####....................",
+".......#........................",
+".......#.....##..###..#..#......",
+".......#....#..#.#..#.#..#......",
+".......#....#..#.#..#.#..#......",
+"........####.##..###...###......",
+".................#.......#......",
+"...###########...#.......#......",
+"...#.........#........###.......",
+"...#.#####.#.#..................",
+"...#.........#..................",
+"...#.###.###.#..................",
+"...#.........#..................",
+"...#.####..###..................",
+"...#......#..#..................",
+"...#...#.....#..................",
+"...#.#######.#.###########......",
+"...#.........#.#.........#......",
+"...###########.#.#####.#.#......",
+"...............#.........#......",
+".......#...#...#.###.###.#......",
+".......#....#..#.........#......",
+"........######.#.####..###......",
+"............#..#......#..#......",
+"...........#...#...#.....#......",
+"...............#.#######.#......",
+"...............#.........#......",
+"...............###########......",
+"................................",
+"................................",
+"................................"
+};
diff --git a/tools/ioemu/gui/bitmaps/floppya.h b/tools/ioemu/gui/bitmaps/floppya.h
new file mode 100644
index 0000000000..86ba90f6f6
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/floppya.h
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: floppya.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#define BX_FLOPPYA_BMAP_X 32
+#define BX_FLOPPYA_BMAP_Y 32
+
+static const unsigned char bx_floppya_bmap[(BX_FLOPPYA_BMAP_X * BX_FLOPPYA_BMAP_Y)/8] = {
+ 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x40, 0x11, 0x00,
+ 0x00, 0xc0, 0x01, 0x00, 0x00, 0x60, 0x13, 0x00, 0x00, 0x60, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0x20, 0xfd, 0xbf, 0x04, 0x20, 0x01, 0x80, 0x04, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x01, 0x80, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x01, 0x80, 0x07, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x01, 0x80, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xaf, 0xea, 0x07, 0xe0, 0xf7, 0xd5, 0x07,
+ 0xe0, 0xef, 0xea, 0x07, 0xe0, 0xf7, 0xd5, 0x07, 0xc0, 0xef, 0xea, 0x07,
+ 0x80, 0x57, 0xd5, 0x07, 0x00, 0xaf, 0xea, 0x07
+ };
+
+static const unsigned char bx_floppya_eject_bmap[(BX_FLOPPYA_BMAP_X * BX_FLOPPYA_BMAP_Y)/8] = {
+ 0x01, 0x80, 0x00, 0x80, 0x02, 0x40, 0x01, 0x40, 0x04, 0x40, 0x11, 0x20,
+ 0x08, 0xc0, 0x01, 0x10, 0x10, 0x60, 0x13, 0x08, 0x20, 0x60, 0x03, 0x04,
+ 0x40, 0x00, 0x00, 0x02, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0x20, 0xff, 0xff, 0x04, 0x20, 0x05, 0xa0, 0x04, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x11, 0x88, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x41, 0x82, 0x07,
+ 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x81, 0x81, 0x07, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x21, 0x84, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x09, 0x90, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xaf, 0xea, 0x07, 0xe0, 0xf7, 0xd5, 0x07,
+ 0xf0, 0xef, 0xea, 0x0f, 0xe8, 0xf7, 0xd5, 0x17, 0xc4, 0xef, 0xea, 0x27,
+ 0x82, 0x57, 0xd5, 0x47, 0x01, 0xaf, 0xea, 0x87
+ };
diff --git a/tools/ioemu/gui/bitmaps/floppya.xpm b/tools/ioemu/gui/bitmaps/floppya.xpm
new file mode 100644
index 0000000000..637f198b9b
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/floppya.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *floppya_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"...............#................",
+"..............#.#...............",
+"..............#.#...#...........",
+"..............###...............",
+".............##.##..#...........",
+".............##.##..............",
+"................................",
+".....######################.....",
+".....####..............####.....",
+".....#..#.############.#..#.....",
+".....#..#..............#..#.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....######################.....",
+".....######################.....",
+".....######################.....",
+".....######################.....",
+".....#######.#.#.#.#.######.....",
+".....######.#####.#.#.#####.....",
+".....#######.###.#.#.######.....",
+".....######.#####.#.#.#####.....",
+"......######.###.#.#.######.....",
+".......####.#.#.#.#.#.#####.....",
+"........####.#.#.#.#.######....."
+};
diff --git a/tools/ioemu/gui/bitmaps/floppyb.h b/tools/ioemu/gui/bitmaps/floppyb.h
new file mode 100644
index 0000000000..70c7597201
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/floppyb.h
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: floppyb.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#define BX_FLOPPYB_BMAP_X 32
+#define BX_FLOPPYB_BMAP_Y 32
+
+static const unsigned char bx_floppyb_bmap[(BX_FLOPPYB_BMAP_X * BX_FLOPPYB_BMAP_Y)/8] = {
+ 0x00, 0xe0, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0xe0, 0x08, 0x00,
+ 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x09, 0x00, 0x00, 0xe0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0x20, 0xfd, 0xbf, 0x04, 0x20, 0x01, 0x80, 0x04, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x01, 0x80, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x01, 0x80, 0x07, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x01, 0x80, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xaf, 0xea, 0x07, 0xe0, 0xf7, 0xd5, 0x07,
+ 0xe0, 0xef, 0xea, 0x07, 0xe0, 0xf7, 0xd5, 0x07, 0xc0, 0xef, 0xea, 0x07,
+ 0x80, 0x57, 0xd5, 0x07, 0x00, 0xaf, 0xea, 0x07
+ };
+
+static const unsigned char bx_floppyb_eject_bmap[(BX_FLOPPYB_BMAP_X * BX_FLOPPYB_BMAP_Y)/8] = {
+ 0x01, 0xe0, 0x00, 0x80, 0x02, 0x20, 0x01, 0x40, 0x04, 0xe0, 0x08, 0x20,
+ 0x08, 0x20, 0x01, 0x10, 0x10, 0x20, 0x09, 0x08, 0x20, 0xe0, 0x00, 0x04,
+ 0x40, 0x00, 0x00, 0x02, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0x01, 0x80, 0x07,
+ 0x20, 0xff, 0xff, 0x04, 0x20, 0x05, 0xa0, 0x04, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x11, 0x88, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x41, 0x82, 0x07,
+ 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x81, 0x81, 0x07, 0xe0, 0xfd, 0xbf, 0x07,
+ 0xe0, 0x21, 0x84, 0x07, 0xe0, 0xfd, 0xbf, 0x07, 0xe0, 0x09, 0x90, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xff, 0xff, 0x07,
+ 0xe0, 0xff, 0xff, 0x07, 0xe0, 0xaf, 0xea, 0x07, 0xe0, 0xf7, 0xd5, 0x07,
+ 0xf0, 0xef, 0xea, 0x0f, 0xe8, 0xf7, 0xd5, 0x17, 0xc4, 0xef, 0xea, 0x27,
+ 0x82, 0x57, 0xd5, 0x47, 0x01, 0xaf, 0xea, 0x87
+ };
diff --git a/tools/ioemu/gui/bitmaps/floppyb.xpm b/tools/ioemu/gui/bitmaps/floppyb.xpm
new file mode 100644
index 0000000000..f4e20e5dcb
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/floppyb.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *floppyb_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+".............###................",
+".............#..#...............",
+".............###...#............",
+".............#..#...............",
+".............#..#..#............",
+".............###................",
+"................................",
+".....######################.....",
+".....####..............####.....",
+".....#..#.############.#..#.....",
+".....#..#..............#..#.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....####.############.####.....",
+".....####..............####.....",
+".....######################.....",
+".....######################.....",
+".....######################.....",
+".....######################.....",
+".....#######.#.#.#.#.######.....",
+".....######.#####.#.#.#####.....",
+".....#######.###.#.#.######.....",
+".....######.#####.#.#.#####.....",
+"......######.###.#.#.######.....",
+".......####.#.#.#.#.#.#####.....",
+"........####.#.#.#.#.######....."
+};
diff --git a/tools/ioemu/gui/bitmaps/mouse.h b/tools/ioemu/gui/bitmaps/mouse.h
new file mode 100644
index 0000000000..cc8f364125
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/mouse.h
@@ -0,0 +1,34 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: mouse.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#define BX_MOUSE_BMAP_X 32
+#define BX_MOUSE_BMAP_Y 32
+
+static unsigned char bx_mouse_bmap[(BX_MOUSE_BMAP_X * BX_MOUSE_BMAP_Y)/8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xf8, 0x1f, 0x00,
+ 0x00, 0x0c, 0x30, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x06, 0x40, 0x00,
+ 0xf8, 0xff, 0xc0, 0x00, 0x0c, 0x80, 0xc1, 0x00, 0x24, 0x22, 0xc1, 0x00,
+ 0x74, 0x77, 0x81, 0x00, 0x74, 0x77, 0x81, 0x01, 0x74, 0x77, 0x81, 0x01,
+ 0x74, 0x77, 0x01, 0x01, 0x74, 0x77, 0x01, 0x03, 0x74, 0x77, 0x01, 0x06,
+ 0x24, 0x22, 0x01, 0x0c, 0x0c, 0x80, 0x01, 0x38, 0x04, 0x00, 0x01, 0x00,
+ 0x0c, 0x80, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x0c, 0x80, 0x01, 0x00,
+ 0x04, 0x00, 0x01, 0x00, 0x0c, 0x80, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00,
+ 0x0c, 0x80, 0x01, 0x00, 0x14, 0x40, 0x01, 0x00, 0x2c, 0xa0, 0x01, 0x00,
+ 0x54, 0x55, 0x01, 0x00, 0xac, 0xaa, 0x01, 0x00, 0xf8, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+static unsigned char bx_nomouse_bmap[(BX_MOUSE_BMAP_X * BX_MOUSE_BMAP_Y)/8] = {
+ 0x01, 0x00, 0x00, 0x80, 0x02, 0xe0, 0x07, 0x40, 0x04, 0xf8, 0x1f, 0x20,
+ 0x08, 0x0c, 0x30, 0x10, 0x10, 0x06, 0x60, 0x08, 0x20, 0x06, 0x40, 0x04,
+ 0xf8, 0xff, 0xc0, 0x02, 0x8c, 0x80, 0xc1, 0x01, 0x24, 0x23, 0xc1, 0x00,
+ 0x74, 0x77, 0xc1, 0x00, 0x74, 0x77, 0xa1, 0x01, 0x74, 0x7f, 0x91, 0x01,
+ 0x74, 0x77, 0x09, 0x01, 0x74, 0x77, 0x05, 0x03, 0x74, 0x77, 0x03, 0x06,
+ 0x24, 0xa2, 0x01, 0x0c, 0x0c, 0x80, 0x01, 0x38, 0x04, 0x40, 0x03, 0x00,
+ 0x0c, 0xa0, 0x05, 0x00, 0x04, 0x10, 0x09, 0x00, 0x0c, 0x88, 0x11, 0x00,
+ 0x04, 0x04, 0x21, 0x00, 0x0c, 0x82, 0x41, 0x00, 0x04, 0x01, 0x81, 0x00,
+ 0x8c, 0x80, 0x01, 0x01, 0x54, 0x40, 0x01, 0x02, 0x2c, 0xa0, 0x01, 0x04,
+ 0x54, 0x55, 0x01, 0x08, 0xac, 0xaa, 0x01, 0x10, 0xfc, 0xff, 0x00, 0x20,
+ 0x02, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x80
+ };
diff --git a/tools/ioemu/gui/bitmaps/mouse.xpm b/tools/ioemu/gui/bitmaps/mouse.xpm
new file mode 100644
index 0000000000..dea33275a2
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/mouse.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *mouse_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+".............######.............",
+"...........##########...........",
+"..........##........##..........",
+".........##..........##.........",
+".........##...........#.........",
+"...#############......##........",
+"..##...........##.....##........",
+"..#..#...#...#..#.....##........",
+"..#.###.###.###.#......#........",
+"..#.###.###.###.#......##.......",
+"..#.###.###.###.#......##.......",
+"..#.###.###.###.#.......#.......",
+"..#.###.###.###.#.......##......",
+"..#.###.###.###.#........##.....",
+"..#..#...#...#..#.........##....",
+"..##...........##..........###..",
+"..#.............#...............",
+"..##...........##...............",
+"..#.............#...............",
+"..##...........##...............",
+"..#.............#...............",
+"..##...........##...............",
+"..#.............#...............",
+"..##...........##...............",
+"..#.#.........#.#...............",
+"..##.#.......#.##...............",
+"..#.#.#.#.#.#.#.#...............",
+"..##.#.#.#.#.#.##...............",
+"...#############................",
+"................................",
+"................................"
+};
diff --git a/tools/ioemu/gui/bitmaps/paste.h b/tools/ioemu/gui/bitmaps/paste.h
new file mode 100644
index 0000000000..f574dad3dc
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/paste.h
@@ -0,0 +1,18 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: paste.h,v 1.1 2002/03/11 15:04:58 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+#define BX_PASTE_BMAP_X 32
+#define BX_PASTE_BMAP_Y 32
+
+static unsigned char bx_paste_bmap[(BX_PASTE_BMAP_X*BX_PASTE_BMAP_Y)] = {
+ 0x00, 0x00, 0x00, 0x00, 0xe0, 0x01, 0x10, 0x00, 0x20, 0x9a, 0x93, 0x03,
+ 0x20, 0x66, 0x78, 0x04, 0xe0, 0xa5, 0xd3, 0x07, 0x20, 0x24, 0x54, 0x00,
+ 0x20, 0xd8, 0x93, 0x03, 0x00, 0x80, 0x01, 0x00, 0x00, 0xc0, 0x02, 0x00,
+ 0x00, 0x7c, 0x3f, 0x00, 0xc0, 0x83, 0xc1, 0x03, 0x20, 0x02, 0x40, 0x04,
+ 0x20, 0x01, 0x80, 0x04, 0x20, 0x01, 0x80, 0x04, 0xa0, 0xff, 0xff, 0x05,
+ 0x20, 0x00, 0x00, 0x04, 0x20, 0x00, 0x00, 0x04, 0x20, 0xf8, 0x3f, 0x04,
+ 0x20, 0x08, 0x20, 0x04, 0x20, 0xe8, 0x2b, 0x04, 0x20, 0x08, 0x20, 0x04,
+ 0x20, 0xe8, 0x2e, 0x04, 0x20, 0x08, 0x20, 0x04, 0x20, 0xe8, 0x39, 0x04,
+ 0x20, 0x08, 0x24, 0x04, 0x20, 0x88, 0x20, 0x04, 0x20, 0xe8, 0x2f, 0x04,
+ 0x20, 0x08, 0x20, 0x04, 0x20, 0xf8, 0x3f, 0x04, 0x20, 0x00, 0x00, 0x04,
+ 0xc0, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, };
diff --git a/tools/ioemu/gui/bitmaps/paste.xpm b/tools/ioemu/gui/bitmaps/paste.xpm
new file mode 100644
index 0000000000..5c83b01dc7
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/paste.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *paste_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+".....####...........#...........",
+".....#...#.##..###..#..###......",
+".....#...##..##....####...#.....",
+".....####.#..#.###..#.#####.....",
+".....#....#..#....#.#.#.........",
+".....#.....##.####..#..###......",
+"...............##...............",
+"..............##.#..............",
+"..........#####.######..........",
+"......####.....##.....####......",
+".....#...#............#...#.....",
+".....#..#..............#..#.....",
+".....#..#..............#..#.....",
+".....#.##################.#.....",
+".....#....................#.....",
+".....#....................#.....",
+".....#.....###########....#.....",
+".....#.....#.........#....#.....",
+".....#.....#.#####.#.#....#.....",
+".....#.....#.........#....#.....",
+".....#.....#.###.###.#....#.....",
+".....#.....#.........#....#.....",
+".....#.....#.####..###....#.....",
+".....#.....#......#..#....#.....",
+".....#.....#...#.....#....#.....",
+".....#.....#.#######.#....#.....",
+".....#.....#.........#....#.....",
+".....#.....###########....#.....",
+".....#....................#.....",
+"......####################......",
+"................................"
+};
diff --git a/tools/ioemu/gui/bitmaps/power.h b/tools/ioemu/gui/bitmaps/power.h
new file mode 100644
index 0000000000..ba72d6a31b
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/power.h
@@ -0,0 +1,20 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: power.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#define BX_POWER_BMAP_X 32
+#define BX_POWER_BMAP_Y 32
+
+static const unsigned char bx_power_bmap[(BX_POWER_BMAP_X * BX_POWER_BMAP_Y)/8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x24, 0x67, 0x66, 0x34, 0xa4, 0x28, 0x92, 0x48, 0x9a, 0xa8, 0xfa, 0x04,
+ 0x82, 0x64, 0x09, 0x04, 0x07, 0xa3, 0x70, 0x0e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xf8, 0x03, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00, 0x0f, 0x1e, 0x00,
+ 0x80, 0x03, 0x38, 0x00, 0xc0, 0x00, 0x60, 0x00, 0xe0, 0xe0, 0xe0, 0x00,
+ 0x60, 0xe0, 0xc0, 0x00, 0x70, 0xe0, 0xc0, 0x01, 0x30, 0xe0, 0x80, 0x01,
+ 0x30, 0xe0, 0x80, 0x01, 0x30, 0xe0, 0x80, 0x01, 0x30, 0xe0, 0x80, 0x01,
+ 0x30, 0xe0, 0x80, 0x01, 0x70, 0xe0, 0xc0, 0x01, 0x60, 0xe0, 0xc0, 0x00,
+ 0xe0, 0xe0, 0xe0, 0x00, 0xc0, 0x00, 0x60, 0x00, 0x80, 0x03, 0x38, 0x00,
+ 0x00, 0x0f, 0x1e, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x00, 0xf8, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
diff --git a/tools/ioemu/gui/bitmaps/power.xpm b/tools/ioemu/gui/bitmaps/power.xpm
new file mode 100644
index 0000000000..ff7e9e8367
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/power.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *power_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+"................................",
+".####...........................",
+"..#..#..###..##..##..##...#.##..",
+"..#..#.#...#.#...#..#..#...#..#.",
+".#.##..#...#.#.#.#.#####..#.....",
+".#.....#..#..##.#..#......#.....",
+"###.....##...#.#....###..###....",
+"................................",
+"...........#######..............",
+".........###########............",
+"........####.....####...........",
+".......###.........###..........",
+"......##.............##.........",
+".....###.....###.....###........",
+".....##......###......##........",
+"....###......###......###.......",
+"....##.......###.......##.......",
+"....##.......###.......##.......",
+"....##.......###.......##.......",
+"....##.......###.......##.......",
+"....##.......###.......##.......",
+"....###......###......###.......",
+".....##......###......##........",
+".....###.....###.....###........",
+"......##.............##.........",
+".......###.........###..........",
+"........####.....####...........",
+".........###########............",
+"...........#######..............",
+"................................",
+"................................"
+};
diff --git a/tools/ioemu/gui/bitmaps/reset.h b/tools/ioemu/gui/bitmaps/reset.h
new file mode 100644
index 0000000000..046f80aeb8
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/reset.h
@@ -0,0 +1,20 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: reset.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#define BX_RESET_BMAP_X 32
+#define BX_RESET_BMAP_Y 32
+
+static const unsigned char bx_reset_bmap[(BX_RESET_BMAP_X * BX_RESET_BMAP_Y)/8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x3c, 0x00, 0x00, 0x10,
+ 0x48, 0x0c, 0xc7, 0x7c, 0x48, 0x92, 0x20, 0x11, 0x34, 0x1f, 0xf3, 0x09,
+ 0x24, 0x41, 0x12, 0x48, 0x6e, 0xce, 0xe1, 0x30, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
+ 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xf0, 0x1f, 0x00, 0x00, 0xe0, 0x3f, 0x00,
+ 0x00, 0xc7, 0x38, 0x00, 0x00, 0x87, 0x38, 0x00, 0x00, 0x07, 0x38, 0x00,
+ 0x00, 0x07, 0x38, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x07, 0x38, 0x00,
+ 0x00, 0x07, 0x38, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0xfe, 0x1f, 0x00,
+ 0x00, 0xfc, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
diff --git a/tools/ioemu/gui/bitmaps/reset.xpm b/tools/ioemu/gui/bitmaps/reset.xpm
new file mode 100644
index 0000000000..d47ea6cbfd
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/reset.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *reset_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+".............................#..",
+"..####......................#...",
+"...#..#...##....###...##..#####.",
+"...#..#..#..#..#.....#..#...#...",
+"..#.##..#####...##..#####..#....",
+"..#..#..#.....#..#..#......#..#.",
+".###.##..###..###....###....##..",
+"................................",
+"................................",
+"................................",
+"................................",
+"................................",
+"...............#................",
+"..............##................",
+".............#######............",
+"............#########...........",
+".............#########..........",
+"........###...##...###..........",
+"........###....#...###..........",
+"........###........###..........",
+"........###........###..........",
+"........###........###..........",
+"........###........###..........",
+"........###........###..........",
+"........##############..........",
+".........############...........",
+"..........##########............",
+"................................",
+"................................",
+"................................",
+"................................"
+};
diff --git a/tools/ioemu/gui/bitmaps/snapshot.h b/tools/ioemu/gui/bitmaps/snapshot.h
new file mode 100644
index 0000000000..cced8ff9e7
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/snapshot.h
@@ -0,0 +1,20 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: snapshot.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#define BX_SNAPSHOT_BMAP_X 32
+#define BX_SNAPSHOT_BMAP_Y 32
+
+static const unsigned char bx_snapshot_bmap[(BX_SNAPSHOT_BMAP_X * BX_SNAPSHOT_BMAP_Y)/8] = {
+ 0x00, 0x00, 0x20, 0x40, 0x77, 0xe6, 0xee, 0xec, 0x91, 0xa8, 0xa2, 0x52,
+ 0x96, 0xac, 0xac, 0x52, 0x94, 0xaa, 0xa8, 0x52, 0xb7, 0xee, 0xae, 0xcc,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x00, 0x00, 0x08, 0x10, 0x00,
+ 0x7c, 0x0f, 0x10, 0x1f, 0xfa, 0x07, 0xa0, 0x2e, 0x42, 0x07, 0xa0, 0x50,
+ 0xa3, 0x03, 0xc0, 0xe0, 0xff, 0xee, 0x77, 0xbf, 0x01, 0xf9, 0x9f, 0x40,
+ 0x01, 0x1d, 0xb8, 0xa0, 0xff, 0xe5, 0xa7, 0xff, 0xff, 0xba, 0x5a, 0xff,
+ 0xff, 0x55, 0xb5, 0xff, 0xff, 0x8d, 0xaa, 0xff, 0xff, 0x16, 0x55, 0xff,
+ 0xff, 0xa2, 0x6a, 0xff, 0xff, 0x46, 0x55, 0xff, 0xff, 0xaa, 0x6a, 0xff,
+ 0xff, 0x56, 0x55, 0xff, 0xfe, 0xae, 0x6a, 0x7f, 0x00, 0x55, 0xb5, 0x00,
+ 0x00, 0xbd, 0xba, 0x00, 0x00, 0xfa, 0x5f, 0x00, 0x00, 0xe4, 0x27, 0x00,
+ 0x00, 0x18, 0x18, 0x00, 0x00, 0xe0, 0x07, 0x00
+ };
diff --git a/tools/ioemu/gui/bitmaps/snapshot.xpm b/tools/ioemu/gui/bitmaps/snapshot.xpm
new file mode 100644
index 0000000000..c17305beb5
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/snapshot.xpm
@@ -0,0 +1,41 @@
+/* XPM */
+static char *snapshot_xpm[] = {
+/* width height num_colors chars_per_pixel */
+" 32 32 2 1",
+/* colors */
+". c None",
+"# c #000000",
+/* pixels */
+".....................#........#.",
+"###.###..##..###.###.###..##.###",
+"#...#..#...#.#.#.#...#.#.#..#.#.",
+".##.#..#..##.#.#..##.#.#.#..#.#.",
+"..#.#..#.#.#.#.#...#.#.#.#..#.#.",
+"###.##.#.###.###.###.#.#..##..##",
+".............#..................",
+"............########............",
+"...........#........#...........",
+"..#####.####........#...#####...",
+".#.########..........#.#.###.#..",
+".#....#.###..........#.#....#.#.",
+"##...#.###............##.....###",
+"########.###.######.###.######.#",
+"#.......#..##########..#......#.",
+"#.......#.###......###.#.....#.#",
+"#########.#..######..#.#########",
+"########.#.###.#.#.##.#.########",
+"#########.#.#.#.#.#.##.#########",
+"#########.##...#.#.#.#.#########",
+"########.##.#...#.#.#.#.########",
+"########.#...#.#.#.#.##.########",
+"########.##...#.#.#.#.#.########",
+"########.#.#.#.#.#.#.##.########",
+"########.##.#.#.#.#.#.#.########",
+".#######.###.#.#.#.#.##.#######.",
+"........#.#.#.#.#.#.##.#........",
+"........#.####.#.#.###.#........",
+".........#.##########.#.........",
+"..........#..######..#..........",
+"...........##......##...........",
+".............######............."
+};
diff --git a/tools/ioemu/gui/bitmaps/userbutton.h b/tools/ioemu/gui/bitmaps/userbutton.h
new file mode 100644
index 0000000000..c93f50664b
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/userbutton.h
@@ -0,0 +1,19 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: userbutton.h,v 1.1 2002/08/09 06:16:43 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+#define BX_USER_BMAP_X 32
+#define BX_USER_BMAP_Y 32
+
+static const unsigned char bx_user_bmap[BX_USER_BMAP_X*BX_USER_BMAP_Y/8] = {
+ 0x00, 0x00, 0x00, 0x00, 0x84, 0x78, 0x9e, 0x07, 0x84, 0x04, 0x82, 0x08,
+ 0x84, 0x04, 0x82, 0x08, 0x84, 0x38, 0x9e, 0x07, 0x84, 0x40, 0x82, 0x01,
+ 0x84, 0x40, 0x82, 0x06, 0x78, 0x3c, 0x9e, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1c,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0xfe, 0xff, 0xff, 0x3f, 0x02, 0x00, 0x00, 0x20,
+ 0xaa, 0xaa, 0x2a, 0x2a, 0x02, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x20,
+ 0xaa, 0xaa, 0xaa, 0x2a, 0x52, 0x55, 0x11, 0x25, 0xaa, 0xaa, 0xaa, 0x2a,
+ 0x52, 0x55, 0x01, 0x25, 0xaa, 0xaa, 0x82, 0x2a, 0x52, 0x55, 0x11, 0x25,
+ 0xaa, 0xbf, 0xaa, 0x2a, 0x02, 0x00, 0x00, 0x20, 0xfe, 0xff, 0xff, 0x3f,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
diff --git a/tools/ioemu/gui/bitmaps/userbutton.xpm b/tools/ioemu/gui/bitmaps/userbutton.xpm
new file mode 100644
index 0000000000..a5d1f99eec
--- /dev/null
+++ b/tools/ioemu/gui/bitmaps/userbutton.xpm
@@ -0,0 +1,40 @@
+/* XPM */
+static char *userbutton_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 2 1",
+". c None",
+"# c #000000",
+/* pixels */
+"................................",
+"..#....#...####..####..####.....",
+"..#....#..#......#.....#...#....",
+"..#....#..#......#.....#...#....",
+"..#....#...###...####..####.....",
+"..#....#......#..#.....##.......",
+"..#....#......#..#.....#.##.....",
+"...####...####...####..#...#....",
+"................................",
+"................................",
+".............................#..",
+"..........................###...",
+".........................#......",
+"........................#.......",
+".......................#........",
+".......................#........",
+".#############################..",
+".#...........................#..",
+".#.#.#.#.#.#.#.#.#.#.#...#.#.#..",
+".#...........................#..",
+".#...........................#..",
+".#.#.#.#.#.#.#.#.#.#.#.#.#.#.#..",
+".#..#.#.#.#.#.#.#...#...#.#..#..",
+".#.#.#.#.#.#.#.#.#.#.#.#.#.#.#..",
+".#..#.#.#.#.#.#.#.......#.#..#..",
+".#.#.#.#.#.#.#.#.#.....#.#.#.#..",
+".#..#.#.#.#.#.#.#...#...#.#..#..",
+".#.#.#.#######.#.#.#.#.#.#.#.#..",
+".#...........................#..",
+".#############################..",
+"................................",
+"................................"
+};
diff --git a/tools/ioemu/gui/gui.cc b/tools/ioemu/gui/gui.cc
new file mode 100644
index 0000000000..a6cd30732a
--- /dev/null
+++ b/tools/ioemu/gui/gui.cc
@@ -0,0 +1,592 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: gui.cc,v 1.73 2003/12/18 20:04:48 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#include <signal.h>
+#include "bochs.h"
+#include "gui/bitmaps/floppya.h"
+#include "gui/bitmaps/floppyb.h"
+#include "gui/bitmaps/mouse.h"
+#include "gui/bitmaps/reset.h"
+#include "gui/bitmaps/power.h"
+#include "gui/bitmaps/snapshot.h"
+#include "gui/bitmaps/copy.h"
+#include "gui/bitmaps/paste.h"
+#include "gui/bitmaps/configbutton.h"
+#include "gui/bitmaps/cdromd.h"
+#include "gui/bitmaps/userbutton.h"
+#if BX_WITH_MACOS
+# include <Disks.h>
+#endif
+
+bx_gui_c *bx_gui = NULL;
+
+#define BX_GUI_THIS bx_gui->
+#define LOG_THIS BX_GUI_THIS
+
+bx_gui_c::bx_gui_c(void)
+{
+ put("GUI"); // Init in specific_init
+ settype(GUILOG);
+}
+
+bx_gui_c::~bx_gui_c()
+{
+}
+
+ void
+bx_gui_c::init(int argc, char **argv, unsigned tilewidth, unsigned tileheight)
+{
+ specific_init(argc, argv, tilewidth, tileheight, BX_HEADER_BAR_Y);
+
+ // Define some bitmaps to use in the headerbar
+ BX_GUI_THIS floppyA_bmap_id = create_bitmap(bx_floppya_bmap,
+ BX_FLOPPYA_BMAP_X, BX_FLOPPYA_BMAP_Y);
+ BX_GUI_THIS floppyA_eject_bmap_id = create_bitmap(bx_floppya_eject_bmap,
+ BX_FLOPPYA_BMAP_X, BX_FLOPPYA_BMAP_Y);
+ BX_GUI_THIS floppyB_bmap_id = create_bitmap(bx_floppyb_bmap,
+ BX_FLOPPYB_BMAP_X, BX_FLOPPYB_BMAP_Y);
+ BX_GUI_THIS floppyB_eject_bmap_id = create_bitmap(bx_floppyb_eject_bmap,
+ BX_FLOPPYB_BMAP_X, BX_FLOPPYB_BMAP_Y);
+ BX_GUI_THIS cdromD_bmap_id = create_bitmap(bx_cdromd_bmap,
+ BX_CDROMD_BMAP_X, BX_CDROMD_BMAP_Y);
+ BX_GUI_THIS cdromD_eject_bmap_id = create_bitmap(bx_cdromd_eject_bmap,
+ BX_CDROMD_BMAP_X, BX_CDROMD_BMAP_Y);
+ BX_GUI_THIS mouse_bmap_id = create_bitmap(bx_mouse_bmap,
+ BX_MOUSE_BMAP_X, BX_MOUSE_BMAP_Y);
+ BX_GUI_THIS nomouse_bmap_id = create_bitmap(bx_nomouse_bmap,
+ BX_MOUSE_BMAP_X, BX_MOUSE_BMAP_Y);
+
+
+ BX_GUI_THIS power_bmap_id = create_bitmap(bx_power_bmap, BX_POWER_BMAP_X, BX_POWER_BMAP_Y);
+ BX_GUI_THIS reset_bmap_id = create_bitmap(bx_reset_bmap, BX_RESET_BMAP_X, BX_RESET_BMAP_Y);
+ BX_GUI_THIS snapshot_bmap_id = create_bitmap(bx_snapshot_bmap, BX_SNAPSHOT_BMAP_X, BX_SNAPSHOT_BMAP_Y);
+ BX_GUI_THIS copy_bmap_id = create_bitmap(bx_copy_bmap, BX_COPY_BMAP_X, BX_COPY_BMAP_Y);
+ BX_GUI_THIS paste_bmap_id = create_bitmap(bx_paste_bmap, BX_PASTE_BMAP_X, BX_PASTE_BMAP_Y);
+ BX_GUI_THIS config_bmap_id = create_bitmap(bx_config_bmap, BX_CONFIG_BMAP_X, BX_CONFIG_BMAP_Y);
+ BX_GUI_THIS user_bmap_id = create_bitmap(bx_user_bmap, BX_USER_BMAP_X, BX_USER_BMAP_Y);
+
+
+ // Add the initial bitmaps to the headerbar, and enable callback routine, for use
+ // when that bitmap is clicked on
+
+ // Floppy A:
+ BX_GUI_THIS floppyA_status = DEV_floppy_get_media_status(0);
+ if (BX_GUI_THIS floppyA_status)
+ BX_GUI_THIS floppyA_hbar_id = headerbar_bitmap(BX_GUI_THIS floppyA_bmap_id,
+ BX_GRAVITY_LEFT, floppyA_handler);
+ else
+ BX_GUI_THIS floppyA_hbar_id = headerbar_bitmap(BX_GUI_THIS floppyA_eject_bmap_id,
+ BX_GRAVITY_LEFT, floppyA_handler);
+
+ // Floppy B:
+ BX_GUI_THIS floppyB_status = DEV_floppy_get_media_status(1);
+ if (BX_GUI_THIS floppyB_status)
+ BX_GUI_THIS floppyB_hbar_id = headerbar_bitmap(BX_GUI_THIS floppyB_bmap_id,
+ BX_GRAVITY_LEFT, floppyB_handler);
+ else
+ BX_GUI_THIS floppyB_hbar_id = headerbar_bitmap(BX_GUI_THIS floppyB_eject_bmap_id,
+ BX_GRAVITY_LEFT, floppyB_handler);
+
+ // CDROM,
+ // valgrinds says that the harddrive object is not be initialised yet,
+ // so we just set the bitmap to ejected for now
+#if 0
+ if (DEV_hd_present()) {
+ Bit32u handle = DEV_hd_get_first_cd_handle();
+ BX_GUI_THIS cdromD_status = DEV_hd_get_cd_media_status(handle);
+ }
+
+ if (BX_GUI_THIS cdromD_status)
+ BX_GUI_THIS cdromD_hbar_id = headerbar_bitmap(BX_GUI_THIS cdromD_bmap_id,
+ BX_GRAVITY_LEFT, cdromD_handler);
+ else
+#endif
+ BX_GUI_THIS cdromD_hbar_id = headerbar_bitmap(BX_GUI_THIS cdromD_eject_bmap_id,
+ BX_GRAVITY_LEFT, cdromD_handler);
+
+ // Mouse button
+ if (bx_options.Omouse_enabled->get ())
+ BX_GUI_THIS mouse_hbar_id = headerbar_bitmap(BX_GUI_THIS mouse_bmap_id,
+ BX_GRAVITY_LEFT, toggle_mouse_enable);
+ else
+ BX_GUI_THIS mouse_hbar_id = headerbar_bitmap(BX_GUI_THIS nomouse_bmap_id,
+ BX_GRAVITY_LEFT, toggle_mouse_enable);
+
+ // These are the buttons on the right side. They are created in order
+ // of right to left.
+
+ // Power button
+ BX_GUI_THIS power_hbar_id = headerbar_bitmap(BX_GUI_THIS power_bmap_id,
+ BX_GRAVITY_RIGHT, power_handler);
+ // Reset button
+ BX_GUI_THIS reset_hbar_id = headerbar_bitmap(BX_GUI_THIS reset_bmap_id,
+ BX_GRAVITY_RIGHT, reset_handler);
+ // Configure button
+ BX_GUI_THIS config_hbar_id = headerbar_bitmap(BX_GUI_THIS config_bmap_id,
+ BX_GRAVITY_RIGHT, config_handler);
+ // Snapshot button
+ BX_GUI_THIS snapshot_hbar_id = headerbar_bitmap(BX_GUI_THIS snapshot_bmap_id,
+ BX_GRAVITY_RIGHT, snapshot_handler);
+ // Paste button
+ BX_GUI_THIS paste_hbar_id = headerbar_bitmap(BX_GUI_THIS paste_bmap_id,
+ BX_GRAVITY_RIGHT, paste_handler);
+ // Copy button
+ BX_GUI_THIS copy_hbar_id = headerbar_bitmap(BX_GUI_THIS copy_bmap_id,
+ BX_GRAVITY_RIGHT, copy_handler);
+ // User button
+ BX_GUI_THIS user_hbar_id = headerbar_bitmap(BX_GUI_THIS user_bmap_id,
+ BX_GRAVITY_RIGHT, userbutton_handler);
+
+ if(bx_options.Otext_snapshot_check->get()) {
+ bx_pc_system.register_timer(this, bx_gui_c::snapshot_checker, (unsigned) 1000000, 1, 1, "snap_chk");
+ }
+
+ BX_GUI_THIS charmap_updated = 0;
+
+ show_headerbar();
+}
+
+void
+bx_gui_c::update_drive_status_buttons (void) {
+ BX_GUI_THIS floppyA_status =
+ DEV_floppy_get_media_status(0)
+ && bx_options.floppya.Ostatus->get ();
+ BX_GUI_THIS floppyB_status =
+ DEV_floppy_get_media_status(1)
+ && bx_options.floppyb.Ostatus->get ();
+ Bit32u handle = DEV_hd_get_first_cd_handle();
+ BX_GUI_THIS cdromD_status = DEV_hd_get_cd_media_status(handle);
+ if (BX_GUI_THIS floppyA_status)
+ replace_bitmap(BX_GUI_THIS floppyA_hbar_id, BX_GUI_THIS floppyA_bmap_id);
+ else {
+#if BX_WITH_MACOS
+ // If we are using the Mac floppy driver, eject the disk
+ // from the floppy drive. This doesn't work in MacOS X.
+ if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+ DiskEject(1);
+#endif
+ replace_bitmap(BX_GUI_THIS floppyA_hbar_id, BX_GUI_THIS floppyA_eject_bmap_id);
+ }
+ if (BX_GUI_THIS floppyB_status)
+ replace_bitmap(BX_GUI_THIS floppyB_hbar_id, BX_GUI_THIS floppyB_bmap_id);
+ else {
+#if BX_WITH_MACOS
+ // If we are using the Mac floppy driver, eject the disk
+ // from the floppy drive. This doesn't work in MacOS X.
+ if (!strcmp(bx_options.floppyb.Opath->getptr (), SuperDrive))
+ DiskEject(1);
+#endif
+ replace_bitmap(BX_GUI_THIS floppyB_hbar_id, BX_GUI_THIS floppyB_eject_bmap_id);
+ }
+ if (BX_GUI_THIS cdromD_status)
+ replace_bitmap(BX_GUI_THIS cdromD_hbar_id, BX_GUI_THIS cdromD_bmap_id);
+ else {
+ replace_bitmap(BX_GUI_THIS cdromD_hbar_id, BX_GUI_THIS cdromD_eject_bmap_id);
+ }
+}
+
+ void
+bx_gui_c::floppyA_handler(void)
+{
+ if (bx_options.floppya.Odevtype->get() == BX_FLOPPY_NONE)
+ return; // no primary floppy device present
+#ifdef WIN32
+ if (strcmp(bx_options.Osel_displaylib->get_choice(bx_options.Osel_displaylib->get()),
+ "rfb")) {
+ // instead of just toggling the status, call win32dialog to bring up
+ // a dialog asking what disk image you want to switch to.
+ int ret = SIM->ask_param (BXP_FLOPPYA_PATH);
+ if (ret > 0) {
+ BX_GUI_THIS update_drive_status_buttons ();
+ }
+ return;
+ }
+#endif
+ BX_GUI_THIS floppyA_status = !BX_GUI_THIS floppyA_status;
+ DEV_floppy_set_media_status(0, BX_GUI_THIS floppyA_status);
+ BX_GUI_THIS update_drive_status_buttons ();
+}
+
+ void
+bx_gui_c::floppyB_handler(void)
+{
+ if (bx_options.floppyb.Odevtype->get() == BX_FLOPPY_NONE)
+ return; // no secondary floppy device present
+#ifdef WIN32
+ if (strcmp(bx_options.Osel_displaylib->get_choice(bx_options.Osel_displaylib->get()),
+ "rfb")) {
+ // instead of just toggling the status, call win32dialog to bring up
+ // a dialog asking what disk image you want to switch to.
+ int ret = SIM->ask_param (BXP_FLOPPYB_PATH);
+ if (ret > 0) {
+ BX_GUI_THIS update_drive_status_buttons ();
+ }
+ return;
+ }
+#endif
+ BX_GUI_THIS floppyB_status = !BX_GUI_THIS floppyB_status;
+ DEV_floppy_set_media_status(1, BX_GUI_THIS floppyB_status);
+ BX_GUI_THIS update_drive_status_buttons ();
+}
+
+ void
+bx_gui_c::cdromD_handler(void)
+{
+ Bit32u handle = DEV_hd_get_first_cd_handle();
+ if (!strcmp(bx_options.Osel_config->get_choice(bx_options.Osel_config->get()),
+ "wx")) {
+ // instead of just toggling the status, call wxWindows to bring up
+ // a dialog asking what disk image you want to switch to.
+ // BBD: for now, find the first cdrom and call ask_param on that.
+ // Since we could have multiple cdroms now, maybe we should be adding
+ // one cdrom button for each?
+ bx_param_c *cdrom = SIM->get_first_cdrom ();
+ if (cdrom == NULL)
+ return; // no cdrom found
+ int ret = SIM->ask_param (cdrom->get_id ());
+ if (ret < 0) return; // cancelled
+ // eject and then insert the disk. If the new path is invalid,
+ // the status will return 0.
+ unsigned status = DEV_hd_set_cd_media_status(handle, 0);
+ printf ("eject disk, new_status is %d\n", status);
+ status = DEV_hd_set_cd_media_status(handle, 1);
+ printf ("insert disk, new_status is %d\n", status);
+ fflush (stdout);
+ BX_GUI_THIS cdromD_status = status;
+ } else {
+ BX_GUI_THIS cdromD_status =
+ DEV_hd_set_cd_media_status(handle, !BX_GUI_THIS cdromD_status);
+ }
+ BX_GUI_THIS update_drive_status_buttons ();
+}
+
+ void
+bx_gui_c::reset_handler(void)
+{
+ BX_INFO(( "system RESET callback." ));
+ bx_pc_system.ResetSignal( PCS_SET ); /* XXX is this right? */
+ for (int i=0; i<BX_SMP_PROCESSORS; i++)
+ BX_CPU(i)->reset(BX_RESET_HARDWARE);
+}
+
+ void
+bx_gui_c::power_handler(void)
+{
+ // the user pressed power button, so there's no doubt they want bochs
+ // to quit. Change panics to fatal for the GUI and then do a panic.
+ bx_user_quit = 1;
+ LOG_THIS setonoff(LOGLEV_PANIC, ACT_FATAL);
+ BX_PANIC (("POWER button turned off."));
+ // shouldn't reach this point, but if you do, QUIT!!!
+ fprintf (stderr, "Bochs is exiting because you pressed the power button.\n");
+ BX_EXIT (1);
+}
+
+Bit32s
+bx_gui_c::make_text_snapshot (char **snapshot, Bit32u *length)
+{
+ Bit8u* raw_snap = NULL;
+ char *clean_snap;
+ unsigned line_addr, txt_addr, txHeight, txWidth;
+
+ DEV_vga_get_text_snapshot(&raw_snap, &txHeight, &txWidth);
+ if (txHeight <= 0) return -1;
+ clean_snap = (char*) malloc(txHeight*(txWidth+2)+1);
+ txt_addr = 0;
+ for (unsigned i=0; i<txHeight; i++) {
+ line_addr = i * txWidth * 2;
+ for (unsigned j=0; j<(txWidth*2); j+=2) {
+ clean_snap[txt_addr++] = raw_snap[line_addr+j];
+ }
+ while ((txt_addr > 0) && (clean_snap[txt_addr-1] == ' ')) txt_addr--;
+#ifdef WIN32
+ if(!(bx_options.Otext_snapshot_check->get())) {
+ clean_snap[txt_addr++] = 13;
+ }
+#endif
+ clean_snap[txt_addr++] = 10;
+ }
+ clean_snap[txt_addr] = 0;
+ *snapshot = clean_snap;
+ *length = txt_addr;
+ return 0;
+}
+
+// create a text snapshot and copy to the system clipboard. On guis that
+// we haven't figured out how to support yet, dump to a file instead.
+ void
+bx_gui_c::copy_handler(void)
+{
+ Bit32u len;
+ char *text_snapshot;
+ if (make_text_snapshot (&text_snapshot, &len) < 0) {
+ BX_INFO(( "copy button failed, mode not implemented"));
+ return;
+ }
+ if (!BX_GUI_THIS set_clipboard_text(text_snapshot, len)) {
+ // platform specific code failed, use portable code instead
+ FILE *fp = fopen("copy.txt", "w");
+ fwrite(text_snapshot, 1, len, fp);
+ fclose(fp);
+ }
+ free(text_snapshot);
+}
+
+// Check the current text snapshot against file snapchk.txt.
+ void
+bx_gui_c::snapshot_checker(void * this_ptr)
+{
+ char filename[BX_PATHNAME_LEN];
+ strcpy(filename,"snapchk.txt");
+ FILE *fp = fopen(filename, "rb");
+ if(fp) {
+ char *text_snapshot;
+ Bit32u len;
+ if (make_text_snapshot (&text_snapshot, &len) < 0) {
+ return;
+ }
+ char *compare_snapshot = (char *) malloc((len+1) * sizeof(char));
+ fread(compare_snapshot, 1, len, fp);
+ fclose(fp);
+ strcpy(filename,"snapmask.txt");
+ fp=fopen(filename, "rb");
+ if(fp) {
+ char *mask_snapshot = (char *) malloc((len+1) * sizeof(char));
+ unsigned i;
+ bx_bool flag = 1;
+ fread(mask_snapshot, 1, len, fp);
+ fclose(fp);
+ for(i=0;i<len;i++) {
+ if((text_snapshot[i] != compare_snapshot[i]) &&
+ (compare_snapshot[i] == mask_snapshot[i])) {
+ flag = 0;
+ break;
+ }
+ }
+ if(flag) {
+ if(!memcmp(text_snapshot,compare_snapshot,len)) {
+ BX_PASS(("Test Passed."));
+ } else {
+ BX_PASS(("Test Passed with Mask."));
+ }
+ }
+ } else {
+ if(!memcmp(text_snapshot,compare_snapshot,len)) {
+ BX_PASS(("Test Passed."));
+ }
+ }
+ free(compare_snapshot);
+ free(text_snapshot);
+ }
+}
+
+// create a text snapshot and dump it to a file
+ void
+bx_gui_c::snapshot_handler(void)
+{
+ char *text_snapshot;
+ Bit32u len;
+ if (make_text_snapshot (&text_snapshot, &len) < 0) {
+ BX_ERROR(( "snapshot button failed, mode not implemented"));
+ return;
+ }
+ //FIXME
+ char filename[BX_PATHNAME_LEN];
+#ifdef WIN32
+ if (strcmp(bx_options.Osel_displaylib->get_choice(bx_options.Osel_displaylib->get()),
+ "rfb")) {
+#else
+ if (!strcmp(bx_options.Osel_config->get_choice(bx_options.Osel_config->get()),
+ "wx")) {
+#endif
+ int ret = SIM->ask_filename (filename, sizeof(filename),
+ "Save snapshot as...", "snapshot.txt",
+ bx_param_string_c::SAVE_FILE_DIALOG);
+ if (ret < 0) { // cancelled
+ free(text_snapshot);
+ return;
+ }
+ } else {
+ strcpy (filename, "snapshot.txt");
+ }
+ FILE *fp = fopen(filename, "wb");
+ fwrite(text_snapshot, 1, len, fp);
+ fclose(fp);
+ free(text_snapshot);
+}
+
+// Read ASCII chars from the system clipboard and paste them into bochs.
+// Note that paste cannot work with the key mapping tables loaded.
+ void
+bx_gui_c::paste_handler(void)
+{
+ Bit32s nbytes;
+ Bit8u *bytes;
+ if (!bx_keymap.isKeymapLoaded ()) {
+ BX_ERROR (("keyboard_mapping disabled, so paste cannot work"));
+ return;
+ }
+ if (!BX_GUI_THIS get_clipboard_text(&bytes, &nbytes)) {
+ BX_ERROR (("paste not implemented on this platform"));
+ return;
+ }
+ BX_INFO (("pasting %d bytes", nbytes));
+ DEV_kbd_paste_bytes (bytes, nbytes);
+}
+
+
+ void
+bx_gui_c::config_handler(void)
+{
+ if (strcmp(bx_options.Osel_displaylib->get_choice(bx_options.Osel_displaylib->get()),
+ "rfb")) {
+ SIM->configuration_interface (NULL, CI_RUNTIME_CONFIG);
+ }
+}
+
+ void
+bx_gui_c::toggle_mouse_enable(void)
+{
+ int old = bx_options.Omouse_enabled->get ();
+ BX_DEBUG (("toggle mouse_enabled, now %d", !old));
+ bx_options.Omouse_enabled->set (!old);
+}
+
+ void
+bx_gui_c::userbutton_handler(void)
+{
+ unsigned shortcut[4];
+ unsigned p;
+ char *user_shortcut;
+ int i, len, ret = 1;
+
+ len = 0;
+#ifdef WIN32
+ if (strcmp(bx_options.Osel_displaylib->get_choice(bx_options.Osel_displaylib->get()),
+ "rfb")) {
+#else
+ if (!strcmp(bx_options.Osel_config->get_choice(bx_options.Osel_config->get()),
+ "wx")) {
+#endif
+ ret = SIM->ask_param (BXP_USER_SHORTCUT);
+ }
+ user_shortcut = bx_options.Ouser_shortcut->getptr();
+ if ((ret > 0) && user_shortcut[0] && (strcmp(user_shortcut, "none"))) {
+ len = 0;
+ p = 0;
+ while ((p < strlen(user_shortcut)) && (len < 3)) {
+ if (!strncmp(user_shortcut+p, "alt", 3)) {
+ shortcut[len++] = BX_KEY_ALT_L;
+ p += 3;
+ } else if (!strncmp(user_shortcut+p, "ctrl", 4)) {
+ shortcut[len++] = BX_KEY_CTRL_L;
+ p += 4;
+ } else if (!strncmp(user_shortcut+p, "del", 3)) {
+ shortcut[len++] = BX_KEY_DELETE;
+ p += 3;
+ } else if (!strncmp(user_shortcut+p, "esc", 3)) {
+ shortcut[len++] = BX_KEY_ESC;
+ p += 3;
+ } else if (!strncmp(user_shortcut+p, "f1", 2)) {
+ shortcut[len++] = BX_KEY_F1;
+ p += 2;
+ } else if (!strncmp(user_shortcut+p, "f4", 2)) {
+ shortcut[len++] = BX_KEY_F4;
+ p += 2;
+ } else if (!strncmp(user_shortcut+p, "tab", 3)) {
+ shortcut[len++] = BX_KEY_TAB;
+ p += 3;
+ } else if (!strncmp(user_shortcut+p, "win", 3)) {
+ shortcut[len++] = BX_KEY_WIN_L;
+ p += 3;
+ } else if (!strncmp(user_shortcut+p, "bksp", 4)) {
+ shortcut[len++] = BX_KEY_BACKSPACE;
+ p += 4;
+ } else {
+ BX_ERROR(("Unknown shortcut %s ignored", user_shortcut));
+ return;
+ }
+ }
+ i = 0;
+ while (i < len) {
+ DEV_kbd_gen_scancode(shortcut[i++]);
+ }
+ i--;
+ while (i >= 0) {
+ DEV_kbd_gen_scancode(shortcut[i--] | BX_KEY_RELEASED);
+ }
+ }
+}
+
+ void
+bx_gui_c::mouse_enabled_changed (bx_bool val)
+{
+ // This is only called when SIM->get_init_done is 1. Note that VAL
+ // is the new value of mouse_enabled, which may not match the old
+ // value which is still in bx_options.Omouse_enabled->get ().
+ BX_DEBUG (("replacing the mouse bitmaps"));
+ if (val)
+ BX_GUI_THIS replace_bitmap(BX_GUI_THIS mouse_hbar_id, BX_GUI_THIS mouse_bmap_id);
+ else
+ BX_GUI_THIS replace_bitmap(BX_GUI_THIS mouse_hbar_id, BX_GUI_THIS nomouse_bmap_id);
+ // give the GUI a chance to respond to the event. Most guis will hide
+ // the native mouse cursor and do something to trap the mouse inside the
+ // bochs VGA display window.
+ BX_GUI_THIS mouse_enabled_changed_specific (val);
+}
+
+void
+bx_gui_c::init_signal_handlers ()
+{
+#if BX_GUI_SIGHANDLER
+ if (bx_gui_sighandler)
+ {
+ Bit32u mask = bx_gui->get_sighandler_mask ();
+ for (Bit32u sig=0; sig<32; sig++)
+ {
+ if (mask & (1<<sig))
+ signal (sig, bx_signal_handler);
+ }
+ }
+#endif
+}
+
+ void
+bx_gui_c::set_text_charmap(Bit8u *fbuffer)
+{
+ memcpy(& BX_GUI_THIS vga_charmap, fbuffer, 0x2000);
+ for (unsigned i=0; i<256; i++) BX_GUI_THIS char_changed[i] = 1;
+ BX_GUI_THIS charmap_updated = 1;
+}
+
+ void
+bx_gui_c::set_text_charbyte(Bit16u address, Bit8u data)
+{
+ BX_GUI_THIS vga_charmap[address] = data;
+ BX_GUI_THIS char_changed[address >> 5] = 1;
+ BX_GUI_THIS charmap_updated = 1;
+}
diff --git a/tools/ioemu/gui/gui.h b/tools/ioemu/gui/gui.h
new file mode 100644
index 0000000000..14a44caa71
--- /dev/null
+++ b/tools/ioemu/gui/gui.h
@@ -0,0 +1,352 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: gui.h,v 1.40 2003/06/28 08:04:31 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+typedef struct {
+ Bit8u cs_start;
+ Bit8u cs_end;
+ Bit16u line_offset;
+ Bit16u line_compare;
+ Bit8u h_panning;
+ Bit8u v_panning;
+ bx_bool line_graphics;
+} bx_vga_tminfo_t;
+
+
+BOCHSAPI extern class bx_gui_c *bx_gui;
+
+
+// The bx_gui_c class provides data and behavior that is common to
+// all guis. Each gui implementation will override the abstract methods.
+class BOCHSAPI bx_gui_c : public logfunctions {
+public:
+ bx_gui_c (void);
+ virtual ~bx_gui_c ();
+ // Define the following functions in the module for your particular GUI
+ // (x.cc, beos.cc, ...)
+ virtual void specific_init(int argc, char **argv,
+ unsigned x_tilesize, unsigned y_tilesize, unsigned header_bar_y) = 0;
+ virtual void text_update(Bit8u *old_text, Bit8u *new_text,
+ unsigned long cursor_x, unsigned long cursor_y,
+ bx_vga_tminfo_t tm_info, unsigned rows) = 0;
+ virtual void graphics_tile_update(Bit8u *snapshot, unsigned x, unsigned y) = 0;
+ virtual void handle_events(void) = 0;
+ virtual void flush(void) = 0;
+ virtual void clear_screen(void) = 0;
+ virtual bx_bool palette_change(unsigned index, unsigned red, unsigned green, unsigned blue) = 0;
+ virtual void dimension_update(unsigned x, unsigned y, unsigned fheight=0, unsigned fwidth=0, unsigned bpp=8) = 0;
+ virtual unsigned create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim) = 0;
+ virtual unsigned headerbar_bitmap(unsigned bmap_id, unsigned alignment, void (*f)(void)) = 0;
+ virtual void replace_bitmap(unsigned hbar_id, unsigned bmap_id) = 0;
+ virtual void show_headerbar(void) = 0;
+ virtual int get_clipboard_text(Bit8u **bytes, Bit32s *nbytes) = 0;
+ virtual int set_clipboard_text(char *snapshot, Bit32u len) = 0;
+ virtual void mouse_enabled_changed_specific (bx_bool val) = 0;
+ virtual void exit(void) = 0;
+ // set_display_mode() changes the mode between the configuration interface
+ // and the simulation. This is primarily intended for display libraries
+ // which have a full-screen mode such as SDL, term, and svgalib. The display
+ // mode is set to DISP_MODE_CONFIG before displaying any configuration menus,
+ // for panics that requires user input, when entering the debugger, etc. It
+ // is set to DISP_MODE_SIM when the Bochs simulation resumes. The
+ // enum is defined in gui/siminterface.h.
+ virtual void set_display_mode (disp_mode_t newmode) { /* default=no action*/ }
+ // These are only needed for the term gui. For all other guis they will
+ // have no effect.
+ // returns 32-bit bitmask in which 1 means the GUI should handle that signal
+ virtual Bit32u get_sighandler_mask () {return 0;}
+ // called when registered signal arrives
+ virtual void sighandler (int sig) {}
+#if BX_USE_IDLE_HACK
+ // this is called from the CPU model when the HLT instruction is executed.
+ virtual void sim_is_idle(void) {}
+#endif
+
+ // The following function(s) are defined already, and your
+ // GUI code calls them
+ static void key_event(Bit32u key);
+ static void set_text_charmap(Bit8u *fbuffer);
+ static void set_text_charbyte(Bit16u address, Bit8u data);
+
+ void init(int argc, char **argv,
+ unsigned x_tilesize, unsigned y_tilesize);
+ void update_drive_status_buttons (void);
+ static void mouse_enabled_changed (bx_bool val);
+ static void init_signal_handlers ();
+
+
+protected:
+ // And these are defined and used privately in gui.cc
+ static void floppyA_handler(void);
+ static void floppyB_handler(void);
+ static void cdromD_handler(void);
+ static void reset_handler(void);
+ static void power_handler(void);
+ static void copy_handler(void);
+ static void paste_handler(void);
+ static void snapshot_handler(void);
+ static void snapshot_checker(void *);
+ static void config_handler(void);
+ static void toggle_mouse_enable(void);
+ static void userbutton_handler(void);
+ static Bit32s make_text_snapshot (char **snapshot, Bit32u *length);
+
+ bx_bool floppyA_status;
+ bx_bool floppyB_status;
+ bx_bool cdromD_status;
+ unsigned floppyA_bmap_id, floppyA_eject_bmap_id, floppyA_hbar_id;
+ unsigned floppyB_bmap_id, floppyB_eject_bmap_id, floppyB_hbar_id;
+ unsigned cdromD_bmap_id, cdromD_eject_bmap_id, cdromD_hbar_id;
+ unsigned power_bmap_id, power_hbar_id;
+ unsigned reset_bmap_id, reset_hbar_id;
+ unsigned copy_bmap_id, copy_hbar_id;
+ unsigned paste_bmap_id, paste_hbar_id;
+ unsigned snapshot_bmap_id, snapshot_hbar_id;
+ unsigned config_bmap_id, config_hbar_id;
+ unsigned mouse_bmap_id, nomouse_bmap_id, mouse_hbar_id;
+ unsigned user_bmap_id, user_hbar_id;
+
+ unsigned char vga_charmap[0x2000];
+ bx_bool charmap_updated;
+ bx_bool char_changed[256];
+ disp_mode_t disp_mode;
+ };
+
+
+// Add this macro in the class declaration of each GUI, to define all the
+// required virtual methods. Example:
+//
+// class bx_rfb_gui_c : public bx_gui_c {
+// public:
+// bx_rfb_gui_c (void) {}
+// DECLARE_GUI_VIRTUAL_METHODS()
+// };
+// Then, each method must be defined later in the file.
+#define DECLARE_GUI_VIRTUAL_METHODS() \
+ virtual void specific_init(int argc, char **argv, \
+ unsigned x_tilesize, unsigned y_tilesize, \
+ unsigned header_bar_y); \
+ virtual void text_update(Bit8u *old_text, Bit8u *new_text, \
+ unsigned long cursor_x, unsigned long cursor_y, \
+ bx_vga_tminfo_t tm_info, unsigned rows); \
+ virtual void graphics_tile_update(Bit8u *snapshot, unsigned x, unsigned y); \
+ virtual void handle_events(void); \
+ virtual void flush(void); \
+ virtual void clear_screen(void); \
+ virtual bx_bool palette_change(unsigned index, \
+ unsigned red, unsigned green, unsigned blue); \
+ virtual void dimension_update(unsigned x, unsigned y, unsigned fheight=0, \
+ unsigned fwidth=0, unsigned bpp=8); \
+ virtual unsigned create_bitmap(const unsigned char *bmap, \
+ unsigned xdim, unsigned ydim); \
+ virtual unsigned headerbar_bitmap(unsigned bmap_id, unsigned alignment, \
+ void (*f)(void)); \
+ virtual void replace_bitmap(unsigned hbar_id, unsigned bmap_id); \
+ virtual void show_headerbar(void); \
+ virtual int get_clipboard_text(Bit8u **bytes, Bit32s *nbytes); \
+ virtual int set_clipboard_text(char *snapshot, Bit32u len); \
+ virtual void mouse_enabled_changed_specific (bx_bool val); \
+ virtual void exit(void); \
+ /* end of DECLARE_GUI_VIRTUAL_METHODS */
+
+#define BX_MAX_PIXMAPS 16
+#define BX_MAX_HEADERBAR_ENTRIES 11
+#define BX_HEADER_BAR_Y 32
+
+// align pixmaps towards left or right side of header bar
+#define BX_GRAVITY_LEFT 10
+#define BX_GRAVITY_RIGHT 11
+
+#define BX_KEY_PRESSED 0x00000000
+#define BX_KEY_RELEASED 0x80000000
+
+#define BX_KEY_UNHANDLED 0x10000000
+
+#define BX_KEY_CTRL_L 0
+#define BX_KEY_SHIFT_L 1
+
+#define BX_KEY_F1 2
+#define BX_KEY_F2 3
+#define BX_KEY_F3 4
+#define BX_KEY_F4 5
+#define BX_KEY_F5 6
+#define BX_KEY_F6 7
+#define BX_KEY_F7 8
+#define BX_KEY_F8 9
+#define BX_KEY_F9 10
+#define BX_KEY_F10 11
+#define BX_KEY_F11 12
+#define BX_KEY_F12 13
+
+#define BX_KEY_CTRL_R 14
+#define BX_KEY_SHIFT_R 15
+#define BX_KEY_CAPS_LOCK 16
+#define BX_KEY_NUM_LOCK 17
+#define BX_KEY_ALT_L 18
+#define BX_KEY_ALT_R 19
+
+#define BX_KEY_A 20
+#define BX_KEY_B 21
+#define BX_KEY_C 22
+#define BX_KEY_D 23
+#define BX_KEY_E 24
+#define BX_KEY_F 25
+#define BX_KEY_G 26
+#define BX_KEY_H 27
+#define BX_KEY_I 28
+#define BX_KEY_J 29
+#define BX_KEY_K 30
+#define BX_KEY_L 31
+#define BX_KEY_M 32
+#define BX_KEY_N 33
+#define BX_KEY_O 34
+#define BX_KEY_P 35
+#define BX_KEY_Q 36
+#define BX_KEY_R 37
+#define BX_KEY_S 38
+#define BX_KEY_T 39
+#define BX_KEY_U 40
+#define BX_KEY_V 41
+#define BX_KEY_W 42
+#define BX_KEY_X 43
+#define BX_KEY_Y 44
+#define BX_KEY_Z 45
+
+#define BX_KEY_0 46
+#define BX_KEY_1 47
+#define BX_KEY_2 48
+#define BX_KEY_3 49
+#define BX_KEY_4 50
+#define BX_KEY_5 51
+#define BX_KEY_6 52
+#define BX_KEY_7 53
+#define BX_KEY_8 54
+#define BX_KEY_9 55
+
+#define BX_KEY_ESC 56
+
+#define BX_KEY_SPACE 57
+#define BX_KEY_SINGLE_QUOTE 58
+#define BX_KEY_COMMA 59
+#define BX_KEY_PERIOD 60
+#define BX_KEY_SLASH 61
+
+#define BX_KEY_SEMICOLON 62
+#define BX_KEY_EQUALS 63
+
+#define BX_KEY_LEFT_BRACKET 64
+#define BX_KEY_BACKSLASH 65
+#define BX_KEY_RIGHT_BRACKET 66
+#define BX_KEY_MINUS 67
+#define BX_KEY_GRAVE 68
+
+#define BX_KEY_BACKSPACE 69
+#define BX_KEY_ENTER 70
+#define BX_KEY_TAB 71
+
+#define BX_KEY_LEFT_BACKSLASH 72
+#define BX_KEY_PRINT 73
+#define BX_KEY_SCRL_LOCK 74
+#define BX_KEY_PAUSE 75
+
+#define BX_KEY_INSERT 76
+#define BX_KEY_DELETE 77
+#define BX_KEY_HOME 78
+#define BX_KEY_END 79
+#define BX_KEY_PAGE_UP 80
+#define BX_KEY_PAGE_DOWN 81
+
+#define BX_KEY_KP_ADD 82
+#define BX_KEY_KP_SUBTRACT 83
+#define BX_KEY_KP_END 84
+#define BX_KEY_KP_DOWN 85
+#define BX_KEY_KP_PAGE_DOWN 86
+#define BX_KEY_KP_LEFT 87
+#define BX_KEY_KP_RIGHT 88
+#define BX_KEY_KP_HOME 89
+#define BX_KEY_KP_UP 90
+#define BX_KEY_KP_PAGE_UP 91
+#define BX_KEY_KP_INSERT 92
+#define BX_KEY_KP_DELETE 93
+#define BX_KEY_KP_5 94
+
+#define BX_KEY_UP 95
+#define BX_KEY_DOWN 96
+#define BX_KEY_LEFT 97
+#define BX_KEY_RIGHT 98
+
+#define BX_KEY_KP_ENTER 99
+#define BX_KEY_KP_MULTIPLY 100
+#define BX_KEY_KP_DIVIDE 101
+
+#define BX_KEY_WIN_L 102
+#define BX_KEY_WIN_R 103
+#define BX_KEY_MENU 104
+
+#define BX_KEY_ALT_SYSREQ 105
+#define BX_KEY_CTRL_BREAK 106
+
+#define BX_KEY_INT_BACK 107
+#define BX_KEY_INT_FORWARD 108
+#define BX_KEY_INT_STOP 109
+#define BX_KEY_INT_MAIL 110
+#define BX_KEY_INT_SEARCH 111
+#define BX_KEY_INT_FAV 112
+#define BX_KEY_INT_HOME 113
+
+#define BX_KEY_POWER_MYCOMP 114
+#define BX_KEY_POWER_CALC 115
+#define BX_KEY_POWER_SLEEP 116
+#define BX_KEY_POWER_POWER 117
+#define BX_KEY_POWER_WAKE 118
+
+#define BX_KEY_NBKEYS 119
+// If you add BX_KEYs Please update
+// - BX_KEY_NBKEYS
+// - the scancodes table in the file iodev/scancodes.cc
+// - the bx_key_symbol table in the file gui/keymap.cc
+
+
+/////////////// GUI plugin support
+
+// Define macro to supply gui plugin code. This macro is called once in GUI to
+// supply the plugin initialization methods. Since it is nearly identical for
+// each gui module, the macro is easier to maintain than pasting the same code
+// in each one.
+//
+// Each gui should declare a class pointer called "theGui" which is derived
+// from bx_gui_c, before calling this macro. For example, the SDL port
+// says:
+// static bx_sdl_gui_c *theGui;
+
+#define IMPLEMENT_GUI_PLUGIN_CODE(gui_name) \
+ int lib##gui_name##_LTX_plugin_init(plugin_t *plugin, \
+ plugintype_t type, int argc, char *argv[]) { \
+ genlog->info("installing %s module as the Bochs GUI", #gui_name); \
+ theGui = new bx_##gui_name##_gui_c (); \
+ bx_gui = theGui; \
+ return(0); /* Success */ \
+ } \
+ void lib##gui_name##_LTX_plugin_fini(void) { }
diff --git a/tools/ioemu/gui/icon_bochs.h b/tools/ioemu/gui/icon_bochs.h
new file mode 100644
index 0000000000..36669fec53
--- /dev/null
+++ b/tools/ioemu/gui/icon_bochs.h
@@ -0,0 +1,40 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: icon_bochs.h,v 1.3 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#define bochs_icon_width 32
+#define bochs_icon_height 32
+static unsigned char bochs_icon_bits[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
+ 0xe0, 0xff, 0xff, 0x07, 0xf8, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x1f,
+ 0xfc, 0xc7, 0xe3, 0x3f, 0xfc, 0xc7, 0xe3, 0x3f, 0xfc, 0xc3, 0xc3, 0x3f,
+ 0xfc, 0xc3, 0xc3, 0x3f, 0xf8, 0xc1, 0x83, 0x1f, 0xf0, 0xc0, 0x03, 0x0f,
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0xf0, 0xc0, 0x03, 0x0f,
+ 0xf8, 0xc1, 0x83, 0x1f, 0xfc, 0xc3, 0xc3, 0x3f, 0xfc, 0xc3, 0xc3, 0x3f,
+ 0xfc, 0xc7, 0xe3, 0x3f, 0xfc, 0xc7, 0xe3, 0x3f, 0xf8, 0xff, 0xff, 0x1f,
+ 0xf8, 0xff, 0xff, 0x1f, 0xe0, 0xff, 0xff, 0x07, 0x00, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
diff --git a/tools/ioemu/gui/icon_bochs.xpm b/tools/ioemu/gui/icon_bochs.xpm
new file mode 100644
index 0000000000..f895743fcf
--- /dev/null
+++ b/tools/ioemu/gui/icon_bochs.xpm
@@ -0,0 +1,45 @@
+/* XPM */
+static char *icon_bochs_xpm[] = {
+/* columns rows colors chars-per-pixel */
+"32 32 7 1",
+" c black",
+". c #800000",
+"X c #808000",
+"o c yellow",
+"O c #808080",
+"+ c #c0c0c0",
+"@ c None",
+/* pixels */
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
+"@@@@@@@@@@. +@@@+ +@@@@@@@@@",
+"@@@@@@@@ Oo @+ .@@@@@@@@",
+"@@@@@@ ooooo ooo. @@@@@@@",
+"@@@@ oooooo. oooooX @@@@@",
+"@+ XoooooO XX ooooooo O@@@",
+"+ oooooO XXXX X ooooooo @@",
+"@ ooo XXXXXX XX ooooooX ",
+"@@. XXXXXXXX XXX Xooooo. ",
+"@@@@ OXXXXXXXXX XXXXXO oO .@",
+"@@@@ .XXXXXXX XXXXXXX. @@@",
+"@+ oo XXXX XXXXXXXX @@@",
+"@ ooooo XXXXXX O",
+"@@O oooooo OXXXX. XX Oooo ",
+"@@@@ .ooooo. XXXXX oooo O@",
+"@@@@ Oooooo XX. .ooo @@@",
+"@@@@ XX oooooo .oooo. @@@@",
+"@@@@ ooXX . ooO o @@@@",
+"@@@@ oooXX. .Xo XX XXo @@@@",
+"@@@@ ooooXXXXXXXo XXXX.XXoo @@@@",
+"@@@+ oooooooooooo XooXXXooo @@@@",
+"@@@. oooooooooooo Xooooooo @@@@",
+"@@@+ oooooooooo XoooooX .@@@@@",
+"@@@@@O XoooooooX ooooo +@@@@@@",
+"@@@@@@@ ooooooX oooX @@@@@@@@",
+"@@@@@@@@@ ooooX oo @@@@@@@@@",
+"@@@@@@@@@@. Ooo. O@@@@@@@@@@",
+"@@@@@@@@@@@@ @@@@@@@@@@@@",
+"@@@@@@@@@@@@@@O O@@@@@@@@@@@@@",
+"@@@@@@@@@@@@@@@@+@@@@@@@@@@@@@@@",
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
+"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"
+};
diff --git a/tools/ioemu/gui/keymap.cc b/tools/ioemu/gui/keymap.cc
new file mode 100644
index 0000000000..8013693c8a
--- /dev/null
+++ b/tools/ioemu/gui/keymap.cc
@@ -0,0 +1,330 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: keymap.cc,v 1.16 2003/10/11 10:43:24 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Todo
+// . Currently supported by sdl, wxGTK and x11. Check if other guis need mapping.
+// . Tables look-up should be optimised.
+//
+
+#include "bochs.h"
+
+// Table of bochs "BX_KEY_*" symbols
+// the table must be in BX_KEY_* order
+char *bx_key_symbol[BX_KEY_NBKEYS] = {
+ "BX_KEY_CTRL_L", "BX_KEY_SHIFT_L", "BX_KEY_F1",
+ "BX_KEY_F2", "BX_KEY_F3", "BX_KEY_F4",
+ "BX_KEY_F5", "BX_KEY_F6", "BX_KEY_F7",
+ "BX_KEY_F8", "BX_KEY_F9", "BX_KEY_F10",
+ "BX_KEY_F11", "BX_KEY_F12", "BX_KEY_CTRL_R",
+ "BX_KEY_SHIFT_R", "BX_KEY_CAPS_LOCK", "BX_KEY_NUM_LOCK",
+ "BX_KEY_ALT_L", "BX_KEY_ALT_R", "BX_KEY_A",
+ "BX_KEY_B", "BX_KEY_C", "BX_KEY_D",
+ "BX_KEY_E", "BX_KEY_F", "BX_KEY_G",
+ "BX_KEY_H", "BX_KEY_I", "BX_KEY_J",
+ "BX_KEY_K", "BX_KEY_L", "BX_KEY_M",
+ "BX_KEY_N", "BX_KEY_O", "BX_KEY_P",
+ "BX_KEY_Q", "BX_KEY_R", "BX_KEY_S",
+ "BX_KEY_T", "BX_KEY_U", "BX_KEY_V",
+ "BX_KEY_W", "BX_KEY_X", "BX_KEY_Y",
+ "BX_KEY_Z", "BX_KEY_0", "BX_KEY_1",
+ "BX_KEY_2", "BX_KEY_3", "BX_KEY_4",
+ "BX_KEY_5", "BX_KEY_6", "BX_KEY_7",
+ "BX_KEY_8", "BX_KEY_9", "BX_KEY_ESC",
+ "BX_KEY_SPACE", "BX_KEY_SINGLE_QUOTE", "BX_KEY_COMMA",
+ "BX_KEY_PERIOD", "BX_KEY_SLASH", "BX_KEY_SEMICOLON",
+ "BX_KEY_EQUALS", "BX_KEY_LEFT_BRACKET", "BX_KEY_BACKSLASH",
+ "BX_KEY_RIGHT_BRACKET", "BX_KEY_MINUS", "BX_KEY_GRAVE",
+ "BX_KEY_BACKSPACE", "BX_KEY_ENTER", "BX_KEY_TAB",
+ "BX_KEY_LEFT_BACKSLASH", "BX_KEY_PRINT", "BX_KEY_SCRL_LOCK",
+ "BX_KEY_PAUSE", "BX_KEY_INSERT", "BX_KEY_DELETE",
+ "BX_KEY_HOME", "BX_KEY_END", "BX_KEY_PAGE_UP",
+ "BX_KEY_PAGE_DOWN", "BX_KEY_KP_ADD", "BX_KEY_KP_SUBTRACT",
+ "BX_KEY_KP_END", "BX_KEY_KP_DOWN", "BX_KEY_KP_PAGE_DOWN",
+ "BX_KEY_KP_LEFT", "BX_KEY_KP_RIGHT", "BX_KEY_KP_HOME",
+ "BX_KEY_KP_UP", "BX_KEY_KP_PAGE_UP", "BX_KEY_KP_INSERT",
+ "BX_KEY_KP_DELETE", "BX_KEY_KP_5", "BX_KEY_UP",
+ "BX_KEY_DOWN", "BX_KEY_LEFT", "BX_KEY_RIGHT",
+ "BX_KEY_KP_ENTER", "BX_KEY_KP_MULTIPLY", "BX_KEY_KP_DIVIDE",
+ "BX_KEY_WIN_L", "BX_KEY_WIN_R", "BX_KEY_MENU",
+ "BX_KEY_ALT_SYSREQ", "BX_KEY_CTRL_BREAK", "BX_KEY_INT_BACK",
+ "BX_KEY_INT_FORWARD", "BX_KEY_INT_STOP", "BX_KEY_INT_MAIL",
+ "BX_KEY_INT_SEARCH", "BX_KEY_INT_FAV", "BX_KEY_INT_HOME",
+ "BX_KEY_POWER_MYCOMP", "BX_KEY_POWER_CALC", "BX_KEY_POWER_SLEEP",
+ "BX_KEY_POWER_POWER", "BX_KEY_POWER_WAKE",
+ };
+
+bx_keymap_c bx_keymap;
+
+#define LOG_THIS bx_keymap.
+
+bx_keymap_c::bx_keymap_c(void)
+{
+ put("KMAP");
+
+ keymapCount = 0;
+ keymapTable = (BXKeyEntry *)NULL;
+
+}
+
+bx_keymap_c::~bx_keymap_c(void)
+{
+ if(keymapTable != NULL) {
+ free(keymapTable);
+ keymapTable = (BXKeyEntry *)NULL;
+ }
+ keymapCount = 0;
+}
+
+ void
+bx_keymap_c::loadKeymap(Bit32u stringToSymbol(const char*))
+{
+ if(bx_options.keyboard.OuseMapping->get()) {
+ loadKeymap(stringToSymbol,bx_options.keyboard.Okeymap->getptr());
+ }
+}
+
+
+bx_bool
+bx_keymap_c::isKeymapLoaded ()
+{
+ return (keymapCount > 0);
+}
+
+
+///////////////////
+// I'll add these to the keymap object in a minute.
+static unsigned char *lineptr = NULL;
+static int lineCount;
+
+static void
+init_parse ()
+{
+ lineCount = 0;
+}
+
+static void
+init_parse_line (char *line_to_parse)
+{
+ // chop off newline
+ lineptr = (unsigned char *)line_to_parse;
+ char *nl;
+ if( (nl = strchr(line_to_parse,'\n')) != NULL) {
+ *nl = 0;
+ }
+}
+
+static Bit32s
+get_next_word (char *output)
+{
+ char *copyp = output;
+ // find first nonspace
+ while (*lineptr && isspace (*lineptr))
+ lineptr++;
+ if (!*lineptr)
+ return -1; // nothing but spaces until end of line
+ if (*lineptr == '#')
+ return -1; // nothing but a comment
+ // copy nonspaces into the output
+ while (*lineptr && !isspace (*lineptr))
+ *copyp++ = *lineptr++;
+ *copyp=0; // null terminate the copy
+ // there must be at least one nonspace, since that's why we stopped the
+ // first loop!
+ BX_ASSERT (copyp != output);
+ return 0;
+}
+
+static Bit32s
+get_next_keymap_line (FILE *fp, char *bxsym, char *modsym, Bit32s *ascii, char *hostsym)
+{
+ char line[256];
+ char buf[256];
+ line[0] = 0;
+ while (1) {
+ lineCount++;
+ if (!fgets(line, sizeof(line)-1, fp)) return -1; // EOF
+ init_parse_line (line);
+ if (get_next_word (bxsym) >= 0) {
+ modsym[0] = 0;
+ char *p;
+ if ((p = strchr (bxsym, '+')) != NULL) {
+ *p = 0; // truncate bxsym.
+ p++; // move one char beyond the +
+ strcpy (modsym, p); // copy the rest to modsym
+ }
+ if (get_next_word (buf) < 0) {
+ BX_PANIC (("keymap line %d: expected 3 columns", lineCount));
+ return -1;
+ }
+ if (buf[0] == '\'' && buf[2] == '\'' && buf[3]==0) {
+ *ascii = (Bit8u) buf[1];
+ } else if (!strcmp(buf, "space")) {
+ *ascii = ' ';
+ } else if (!strcmp(buf, "return")) {
+ *ascii = '\n';
+ } else if (!strcmp(buf, "tab")) {
+ *ascii = '\t';
+ } else if (!strcmp(buf, "backslash")) {
+ *ascii = '\\';
+ } else if (!strcmp(buf, "apostrophe")) {
+ *ascii = '\'';
+ } else if (!strcmp(buf, "none")) {
+ *ascii = -1;
+ } else {
+ BX_PANIC (("keymap line %d: ascii equivalent is \"%s\" but it must be char constant like 'x', or one of space,tab,return,none", lineCount, buf));
+ }
+ if (get_next_word (hostsym) < 0) {
+ BX_PANIC (("keymap line %d: expected 3 columns", lineCount));
+ return -1;
+ }
+ return 0;
+ }
+ // no words on this line, keep reading.
+ }
+}
+
+ void
+bx_keymap_c::loadKeymap(Bit32u stringToSymbol(const char*), const char* filename)
+{
+ FILE *keymapFile;
+ char baseSym[256], modSym[256], hostSym[256];
+ Bit32s ascii;
+ Bit32u baseKey, modKey, hostKey;
+ struct stat status;
+
+ if (stat(filename, &status)) {
+ BX_PANIC(("Can not stat keymap file '%s'.",filename));
+ }
+
+ if (!(S_ISREG(status.st_mode))) {
+ BX_PANIC(("Keymap file '%s' is not a file",filename));
+ }
+
+ if((keymapFile = fopen(filename,"r"))==NULL) {
+ BX_PANIC(("Can not open keymap file '%s'.",filename));
+ }
+
+ BX_INFO(("Loading keymap from '%s'",filename));
+ init_parse ();
+
+ // Read keymap file one line at a time
+ while(1) {
+ if (get_next_keymap_line (keymapFile,
+ baseSym, modSym, &ascii, hostSym) < 0) { break; }
+
+
+ // convert X_KEY_* symbols to values
+ baseKey = convertStringToBXKey(baseSym);
+ modKey = convertStringToBXKey(modSym);
+ hostKey = 0;
+ if (stringToSymbol != NULL)
+ hostKey = stringToSymbol(hostSym);
+
+ BX_DEBUG (("baseKey='%s' (%d), modSym='%s' (%d), ascii=%d, guisym='%s' (%d)", baseSym, baseKey, modSym, modKey, ascii, hostSym, hostKey));
+
+ // Check if data is valid
+ if( baseKey==BX_KEYMAP_UNKNOWN ) {
+ BX_PANIC (("line %d: unknown BX_KEY constant '%s'",lineCount,baseSym));
+ continue;
+ }
+
+ if( hostKey==BX_KEYMAP_UNKNOWN ) {
+ BX_PANIC (("line %d: unknown host key name '%s'",lineCount,hostSym));
+ continue;
+ }
+
+ keymapTable=(BXKeyEntry*)realloc(keymapTable,(keymapCount+1) * sizeof(BXKeyEntry));
+
+ if(keymapTable==NULL)
+ BX_PANIC(("Can not allocate memory for keymap table."));
+
+ keymapTable[keymapCount].baseKey=baseKey;
+ keymapTable[keymapCount].modKey=modKey;
+ keymapTable[keymapCount].ascii=ascii;
+ keymapTable[keymapCount].hostKey=hostKey;
+
+ keymapCount++;
+ }
+
+ BX_INFO(("Loaded %d symbols",keymapCount));
+
+ fclose(keymapFile);
+}
+
+ Bit32u
+bx_keymap_c::convertStringToBXKey(const char* string)
+{
+ Bit16u i;
+
+ // We look through the bx_key_symbol table to find the searched string
+ for (i=0; i<BX_KEY_NBKEYS; i++) {
+ if (strcmp(string,bx_key_symbol[i])==0) {
+ return i;
+ }
+ }
+
+ // Key is not known
+ return BX_KEYMAP_UNKNOWN;
+}
+
+ BXKeyEntry *
+bx_keymap_c::findHostKey(Bit32u key)
+{
+ Bit16u i;
+
+ // We look through the keymap table to find the searched key
+ for (i=0; i<keymapCount; i++) {
+ if (keymapTable[i].hostKey == key) {
+ BX_DEBUG (("key 0x%02x matches hostKey for entry #%d", key, i));
+ return &keymapTable[i];
+ }
+ }
+ BX_DEBUG (("key %02x matches no entries", key));
+
+ // Return default
+ return NULL;
+}
+
+ BXKeyEntry *
+bx_keymap_c::findAsciiChar(Bit8u ch)
+{
+ Bit16u i;
+ BX_DEBUG (("findAsciiChar (0x%02x)", ch));
+
+ // We look through the keymap table to find the searched key
+ for (i=0; i<keymapCount; i++) {
+ if (keymapTable[i].ascii == ch) {
+ BX_DEBUG (("key %02x matches ascii for entry #%d", ch, i));
+ return &keymapTable[i];
+ }
+ }
+ BX_DEBUG (("key 0x%02x matches no entries", ch));
+
+ // Return default
+ return NULL;
+}
+
+ char *
+bx_keymap_c::getBXKeyName(Bit32u key)
+{
+ return bx_key_symbol[key & 0x7fffffff];
+}
diff --git a/tools/ioemu/gui/keymap.h b/tools/ioemu/gui/keymap.h
new file mode 100644
index 0000000000..945729bd17
--- /dev/null
+++ b/tools/ioemu/gui/keymap.h
@@ -0,0 +1,77 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: keymap.h,v 1.9 2003/07/12 08:17:10 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+/////////////////////////////////////////////////////////////////////////
+//
+// Methods of bx_keymap_c :
+//
+// - loadKeymap(Bit32u convertStringToSymbol(const char*));
+// loads the configuration specified keymap file if keymapping is enabled
+// using convertStringToSymbol to convert strings to client constants
+//
+// - loadKeymap(Bit32u convertStringToSymbol(const char*), const char* filename);
+// loads the specified keymap file
+// using convertStringToSymbol to convert strings to client constants
+//
+// - isKeymapLoaded () returns true if the keymap contains any valid key
+// entries.
+//
+// - convertStringToBXKey
+// convert a null-terminate string to a BX_KEY code
+//
+// - findHostKey(Bit32u key)
+// - findAsciiChar(Bit8u ch)
+// Each of these methods returns a pointer to a BXKeyEntry structure
+// corresponding to a key. findHostKey() finds an entry whose hostKey
+// value matches the target value, and findAsciiChar() finds an entry
+// whose ASCII code matches the search value.
+
+// In case of unknown symbol
+#define BX_KEYMAP_UNKNOWN 0xFFFFFFFF
+
+// Structure of an element of the keymap table
+typedef struct BOCHSAPI {
+ Bit32u baseKey; // base key
+ Bit32u modKey; // modifier key that must be held down
+ Bit32s ascii; // ascii equivalent, if any
+ Bit32u hostKey; // value that the host's OS or library recognizes
+ } BXKeyEntry;
+
+class BOCHSAPI bx_keymap_c : public logfunctions {
+public:
+ bx_keymap_c(void);
+ ~bx_keymap_c(void);
+
+ void loadKeymap(Bit32u(*)(const char*));
+ void loadKeymap(Bit32u(*)(const char*),const char *filename);
+ bx_bool isKeymapLoaded ();
+
+ BXKeyEntry *findHostKey(Bit32u hostkeynum);
+ BXKeyEntry *findAsciiChar(Bit8u ascii);
+ char *getBXKeyName(Bit32u key);
+
+private:
+ Bit32u convertStringToBXKey(const char *);
+
+ BXKeyEntry *keymapTable;
+ Bit16u keymapCount;
+ };
+
+BOCHSAPI extern bx_keymap_c bx_keymap;
diff --git a/tools/ioemu/gui/keymaps/convertmap.pl b/tools/ioemu/gui/keymaps/convertmap.pl
new file mode 100644
index 0000000000..18c47bea93
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/convertmap.pl
@@ -0,0 +1,14 @@
+#!/usr/bin/perl
+# little utility script that I used to convert key map files from
+# the pre-March 11 format to the post-March 11 format. It doesn't
+# do anything smart with the ascii equivalents and modifiers, so ATM those must
+# be added by hand.
+
+while (<STDIN>)
+{
+ chop;
+ s/^ *//;
+ if (/^#/ || /^ *$/) { print "$_\n"; next;}
+ ($key, $equals, $xksym) = split (/ +/);
+ printf ("%-45s %-10s %s\n", $key, 'none', "XK_$xksym");
+}
diff --git a/tools/ioemu/gui/keymaps/sdl-pc-de.map b/tools/ioemu/gui/keymaps/sdl-pc-de.map
new file mode 100644
index 0000000000..de4fd59cd8
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/sdl-pc-de.map
@@ -0,0 +1,222 @@
+# Bochs Keymap file
+# $Id: sdl-pc-de.map,v 1.2 2002/10/24 21:06:55 bdenney Exp $
+# Target: PC(x86) keyboard, DE keymap, SDL gui on X11
+# Author: Volker Ruppert
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Host_key_name
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Host_key_name
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Host_key_name is the name of the key combination according to the gui library
+# (X windows, SDL, etc). Each GUI module must provide a function that converts
+# these host key names into numbers. A pointer to the conversion function is
+# passed to loadKeymap(), and it is used when parsing the keymap file. As the
+# keymap file is parsed, the conversion function is called for each host key
+# name, to convert it into a number. Only the number is stored. If the host
+# key name is not found, the conversion function returns BX_KEYMAP_UNKNOWN, and
+# the keymap code will panic, like this:
+#
+# [KMAP ] line 51: unknown host key name 'SDLK_PAREN_RIGHT'
+#
+# If this happens, you must edit the keymap file, and either correct the host
+# key name or comment out that line.
+#
+
+BX_KEY_0 '0' SDLK_0
+BX_KEY_0+BX_KEY_SHIFT_L '=' SDLK_EQUALS
+BX_KEY_1 '1' SDLK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' SDLK_EXCLAIM
+BX_KEY_2 '2' SDLK_2
+BX_KEY_2+BX_KEY_ALT_R '²' SDLK_2
+BX_KEY_2+BX_KEY_SHIFT_L '"' SDLK_QUOTEDBL
+BX_KEY_3 '3' SDLK_3
+BX_KEY_3+BX_KEY_SHIFT_L '§' SDLK_3
+BX_KEY_4 '4' SDLK_4
+BX_KEY_4+BX_KEY_SHIFT_L '$' SDLK_DOLLAR
+BX_KEY_4+BX_KEY_ALT_R '¼' SDLK_4
+BX_KEY_5 '5' SDLK_5
+BX_KEY_5+BX_KEY_ALT_R '½' SDLK_5
+BX_KEY_5+BX_KEY_SHIFT_L '%' SDLK_5
+BX_KEY_6 '6' SDLK_6
+BX_KEY_6+BX_KEY_SHIFT_L '&' SDLK_AMPERSAND
+BX_KEY_7 '7' SDLK_7
+BX_KEY_7+BX_KEY_ALT_R '{' SDLK_7
+BX_KEY_7+BX_KEY_SHIFT_L '/' SDLK_SLASH
+BX_KEY_8 '8' SDLK_8
+BX_KEY_8+BX_KEY_ALT_R '[' SDLK_LEFTBRACKET
+BX_KEY_8+BX_KEY_SHIFT_L '(' SDLK_LEFTPAREN
+BX_KEY_9 '9' SDLK_9
+BX_KEY_9+BX_KEY_ALT_R ']' SDLK_RIGHTBRACKET
+BX_KEY_9+BX_KEY_SHIFT_L ')' SDLK_RIGHTPAREN
+BX_KEY_A+BX_KEY_SHIFT_L 'A' SDLK_a
+BX_KEY_A 'a' SDLK_a
+BX_KEY_A+BX_KEY_ALT_R 'æ' SDLK_a
+BX_KEY_B+BX_KEY_SHIFT_L 'B' SDLK_b
+BX_KEY_B 'b' SDLK_b
+BX_KEY_C+BX_KEY_SHIFT_L 'C' SDLK_c
+BX_KEY_C 'c' SDLK_c
+BX_KEY_C+BX_KEY_ALT_R '¢' SDLK_c
+BX_KEY_D+BX_KEY_SHIFT_L 'D' SDLK_d
+BX_KEY_D 'd' SDLK_d
+BX_KEY_E+BX_KEY_SHIFT_L 'E' SDLK_e
+BX_KEY_E+BX_KEY_ALT_R none SDLK_EURO
+BX_KEY_E 'e' SDLK_e
+BX_KEY_F+BX_KEY_SHIFT_L 'F' SDLK_f
+BX_KEY_F 'f' SDLK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' SDLK_g
+BX_KEY_G 'g' SDLK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' SDLK_h
+BX_KEY_H 'h' SDLK_h
+BX_KEY_I+BX_KEY_SHIFT_L 'I' SDLK_i
+BX_KEY_I 'i' SDLK_i
+BX_KEY_J+BX_KEY_SHIFT_L 'J' SDLK_j
+BX_KEY_J 'j' SDLK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' SDLK_k
+BX_KEY_K 'k' SDLK_k
+BX_KEY_L+BX_KEY_SHIFT_L 'L' SDLK_l
+BX_KEY_L 'l' SDLK_l
+BX_KEY_M+BX_KEY_SHIFT_L 'M' SDLK_m
+BX_KEY_M 'm' SDLK_m
+BX_KEY_M+BX_KEY_ALT_R 'µ' SDLK_m
+BX_KEY_N+BX_KEY_SHIFT_L 'N' SDLK_n
+BX_KEY_N 'n' SDLK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' SDLK_o
+BX_KEY_O 'o' SDLK_o
+BX_KEY_O+BX_KEY_ALT_R 'ø' SDLK_o
+BX_KEY_P+BX_KEY_SHIFT_L 'P' SDLK_p
+BX_KEY_P 'p' SDLK_p
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' SDLK_q
+BX_KEY_Q+BX_KEY_ALT_R '@' SDLK_AT
+BX_KEY_Q 'q' SDLK_q
+BX_KEY_R+BX_KEY_SHIFT_L 'R' SDLK_r
+BX_KEY_R+BX_KEY_ALT_R '¶' SDLK_r
+BX_KEY_R 'r' SDLK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' SDLK_s
+BX_KEY_S 's' SDLK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' SDLK_t
+BX_KEY_T 't' SDLK_t
+BX_KEY_U+BX_KEY_SHIFT_L 'U' SDLK_u
+BX_KEY_U 'u' SDLK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' SDLK_v
+BX_KEY_V 'v' SDLK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'W' SDLK_w
+BX_KEY_W 'w' SDLK_w
+BX_KEY_X+BX_KEY_SHIFT_L 'X' SDLK_x
+BX_KEY_X+BX_KEY_ALT_R '»' SDLK_x
+BX_KEY_X 'x' SDLK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Z' SDLK_z
+BX_KEY_Y 'z' SDLK_z
+BX_KEY_Z+BX_KEY_SHIFT_L 'Y' SDLK_y
+BX_KEY_Z+BX_KEY_ALT_R '«' SDLK_y
+BX_KEY_Z 'y' SDLK_y
+BX_KEY_F1 none SDLK_F1
+BX_KEY_F2 none SDLK_F2
+BX_KEY_F3 none SDLK_F3
+BX_KEY_F4 none SDLK_F4
+BX_KEY_F5 none SDLK_F5
+BX_KEY_F6 none SDLK_F6
+BX_KEY_F7 none SDLK_F7
+BX_KEY_F8 none SDLK_F8
+BX_KEY_F9 none SDLK_F9
+BX_KEY_F10 none SDLK_F10
+BX_KEY_F11 none SDLK_F11
+BX_KEY_F12 none SDLK_F12
+BX_KEY_ALT_L none SDLK_LALT
+BX_KEY_ALT_L none SDLK_LMETA
+BX_KEY_ALT_R none SDLK_RALT
+BX_KEY_ALT_R none SDLK_MODE
+BX_KEY_BACKSLASH apostrophe SDLK_QUOTE
+BX_KEY_BACKSLASH '#' SDLK_HASH
+BX_KEY_BACKSPACE none SDLK_BACKSPACE
+BX_KEY_CAPS_LOCK none SDLK_CAPSLOCK
+BX_KEY_COMMA ',' SDLK_COMMA
+BX_KEY_COMMA+BX_KEY_SHIFT_L ';' SDLK_SEMICOLON
+BX_KEY_CTRL_L none SDLK_LCTRL
+BX_KEY_CTRL_R none SDLK_RCTRL
+BX_KEY_DELETE none SDLK_DELETE
+BX_KEY_DOWN none SDLK_DOWN
+BX_KEY_END none SDLK_END
+BX_KEY_ENTER return SDLK_RETURN
+BX_KEY_EQUALS none SDLK_WORLD_20
+BX_KEY_EQUALS+BX_KEY_ALT_R '¸' SDLK_WORLD_20
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '`' SDLK_WORLD_20
+BX_KEY_ESC none SDLK_ESCAPE
+BX_KEY_GRAVE '^' SDLK_CARET
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '°' SDLK_CARET
+BX_KEY_GRAVE+BX_KEY_ALT_R '¬' SDLK_CARET
+BX_KEY_HOME none SDLK_HOME
+BX_KEY_INSERT none SDLK_INSERT
+BX_KEY_KP_5 none SDLK_KP5
+BX_KEY_KP_ADD none SDLK_KP_PLUS
+BX_KEY_KP_DELETE none SDLK_KP_PERIOD
+BX_KEY_KP_DIVIDE none SDLK_KP_DIVIDE
+BX_KEY_KP_DOWN none SDLK_KP2
+BX_KEY_KP_END none SDLK_KP1
+BX_KEY_KP_ENTER none SDLK_KP_ENTER
+BX_KEY_KP_HOME none SDLK_KP7
+BX_KEY_KP_INSERT none SDLK_KP0
+BX_KEY_KP_LEFT none SDLK_KP4
+BX_KEY_KP_MULTIPLY none SDLK_KP_MULTIPLY
+BX_KEY_KP_PAGE_DOWN none SDLK_KP3
+BX_KEY_KP_PAGE_UP none SDLK_KP9
+BX_KEY_KP_RIGHT none SDLK_KP6
+BX_KEY_KP_SUBTRACT none SDLK_KP_MINUS
+BX_KEY_KP_UP none SDLK_KP8
+BX_KEY_LEFT none SDLK_LEFT
+BX_KEY_LEFT_BACKSLASH+BX_KEY_ALT_R '|' SDLK_LESS
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '>' SDLK_GREATER
+BX_KEY_LEFT_BACKSLASH '<' SDLK_LESS
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L 'Ü' SDLK_WORLD_92
+BX_KEY_LEFT_BRACKET 'ü' SDLK_WORLD_92
+BX_KEY_MENU none SDLK_MENU
+BX_KEY_MINUS+BX_KEY_ALT_L backslash SDLK_BACKSLASH
+BX_KEY_MINUS+BX_KEY_SHIFT_L '?' SDLK_QUESTION
+BX_KEY_MINUS 'ß' SDLK_WORLD_63
+BX_KEY_NUM_LOCK none SDLK_NUMLOCK
+BX_KEY_PAGE_DOWN none SDLK_PAGEDOWN
+BX_KEY_PAGE_UP none SDLK_PAGEUP
+BX_KEY_PAUSE none SDLK_BREAK
+BX_KEY_PAUSE none SDLK_PAUSE
+BX_KEY_PERIOD+BX_KEY_SHIFT_L ':' SDLK_COLON
+BX_KEY_PERIOD '.' SDLK_PERIOD
+BX_KEY_PERIOD+BX_KEY_ALT_L '·' SDLK_PERIOD
+BX_KEY_PRINT none SDLK_PRINT
+BX_KEY_PRINT none SDLK_SYSREQ
+BX_KEY_RIGHT none SDLK_RIGHT
+BX_KEY_RIGHT_BRACKET+BX_KEY_ALT_R '~' SDLK_PLUS
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '*' SDLK_PLUS
+BX_KEY_RIGHT_BRACKET '+' SDLK_PLUS
+BX_KEY_SCRL_LOCK none SDLK_SCROLLOCK
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L 'Ö' SDLK_WORLD_86
+BX_KEY_SEMICOLON 'ö' SDLK_WORLD_86
+BX_KEY_SHIFT_L none SDLK_LSHIFT
+BX_KEY_SHIFT_R none SDLK_RSHIFT
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L 'Ä' SDLK_WORLD_68
+BX_KEY_SINGLE_QUOTE 'ä' SDLK_WORLD_68
+BX_KEY_SLASH '-' SDLK_MINUS
+BX_KEY_SLASH+BX_KEY_SHIFT_L '_' SDLK_UNDERSCORE
+BX_KEY_SPACE space SDLK_SPACE
+BX_KEY_TAB tab SDLK_TAB
+BX_KEY_UP none SDLK_UP
+BX_KEY_WIN_L none SDLK_LSUPER
+BX_KEY_WIN_R none SDLK_RSUPER
diff --git a/tools/ioemu/gui/keymaps/sdl-pc-us.map b/tools/ioemu/gui/keymaps/sdl-pc-us.map
new file mode 100644
index 0000000000..3440992b50
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/sdl-pc-us.map
@@ -0,0 +1,211 @@
+# Bochs Keymap file
+# $Id: sdl-pc-us.map,v 1.2 2002/10/24 21:06:55 bdenney Exp $
+# Target: PC(x86) keyboard, US keymap, SDL gui
+# Author: Bryce Denney
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Host_key_name
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Host_key_name
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Host_key_name is the name of the key combination according to the gui library
+# (X windows, SDL, etc). Each GUI module must provide a function that converts
+# these host key names into numbers. A pointer to the conversion function is
+# passed to loadKeymap(), and it is used when parsing the keymap file. As the
+# keymap file is parsed, the conversion function is called for each host key
+# name, to convert it into a number. Only the number is stored. If the host
+# key name is not found, the conversion function returns BX_KEYMAP_UNKNOWN, and
+# the keymap code will panic, like this:
+#
+# [KMAP ] line 51: unknown host key name 'SDLK_PAREN_RIGHT'
+#
+# If this happens, you must edit the keymap file, and either correct the host
+# key name or comment out that line.
+#
+
+BX_KEY_0 '0' SDLK_0
+BX_KEY_0+BX_KEY_SHIFT_L ')' SDLK_RIGHTPAREN
+BX_KEY_1 '1' SDLK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' SDLK_EXCLAIM
+BX_KEY_2 '2' SDLK_2
+BX_KEY_2+BX_KEY_SHIFT_L '@' SDLK_AT
+BX_KEY_3 '3' SDLK_3
+BX_KEY_3+BX_KEY_SHIFT_L '#' SDLK_HASH
+BX_KEY_4 '4' SDLK_4
+BX_KEY_4+BX_KEY_SHIFT_L '$' SDLK_DOLLAR
+BX_KEY_5 '5' SDLK_5
+#BX_KEY_5+BX_KEY_SHIFT_L '%' SDLK_PERCENT
+BX_KEY_6 '6' SDLK_6
+BX_KEY_6+BX_KEY_SHIFT_L '^' SDLK_CARET
+BX_KEY_7 '7' SDLK_7
+BX_KEY_7+BX_KEY_SHIFT_L '&' SDLK_AMPERSAND
+BX_KEY_8 '8' SDLK_8
+BX_KEY_8+BX_KEY_SHIFT_L '*' SDLK_ASTERISK
+BX_KEY_9 '9' SDLK_9
+BX_KEY_9+BX_KEY_SHIFT_L '(' SDLK_LEFTPAREN
+BX_KEY_A+BX_KEY_SHIFT_L 'A' SDLK_a
+BX_KEY_A 'a' SDLK_a
+BX_KEY_B+BX_KEY_SHIFT_L 'B' SDLK_b
+BX_KEY_B 'b' SDLK_b
+BX_KEY_C+BX_KEY_SHIFT_L 'C' SDLK_c
+BX_KEY_C 'c' SDLK_c
+BX_KEY_D+BX_KEY_SHIFT_L 'D' SDLK_d
+BX_KEY_D 'd' SDLK_d
+BX_KEY_E+BX_KEY_SHIFT_L 'E' SDLK_e
+BX_KEY_E 'e' SDLK_e
+BX_KEY_F+BX_KEY_SHIFT_L 'F' SDLK_f
+BX_KEY_F 'f' SDLK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' SDLK_g
+BX_KEY_G 'g' SDLK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' SDLK_h
+BX_KEY_H 'h' SDLK_h
+BX_KEY_I+BX_KEY_SHIFT_L 'I' SDLK_i
+BX_KEY_I 'i' SDLK_i
+BX_KEY_J+BX_KEY_SHIFT_L 'J' SDLK_j
+BX_KEY_J 'j' SDLK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' SDLK_k
+BX_KEY_K 'k' SDLK_k
+BX_KEY_L+BX_KEY_SHIFT_L 'L' SDLK_l
+BX_KEY_L 'l' SDLK_l
+BX_KEY_M+BX_KEY_SHIFT_L 'M' SDLK_m
+BX_KEY_M 'm' SDLK_m
+BX_KEY_N+BX_KEY_SHIFT_L 'N' SDLK_n
+BX_KEY_N 'n' SDLK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' SDLK_o
+BX_KEY_O 'o' SDLK_o
+BX_KEY_P+BX_KEY_SHIFT_L 'P' SDLK_p
+BX_KEY_P 'p' SDLK_p
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' SDLK_q
+BX_KEY_Q 'q' SDLK_q
+BX_KEY_R+BX_KEY_SHIFT_L 'R' SDLK_r
+BX_KEY_R 'r' SDLK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' SDLK_s
+BX_KEY_S 's' SDLK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' SDLK_t
+BX_KEY_T 't' SDLK_t
+BX_KEY_U+BX_KEY_SHIFT_L 'U' SDLK_u
+BX_KEY_U 'u' SDLK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' SDLK_v
+BX_KEY_V 'v' SDLK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'W' SDLK_w
+BX_KEY_W 'w' SDLK_w
+BX_KEY_X+BX_KEY_SHIFT_L 'X' SDLK_x
+BX_KEY_X 'x' SDLK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' SDLK_y
+BX_KEY_Y 'y' SDLK_y
+BX_KEY_Z+BX_KEY_SHIFT_L 'Z' SDLK_z
+BX_KEY_Z 'z' SDLK_z
+BX_KEY_F1 none SDLK_F1
+BX_KEY_F2 none SDLK_F2
+BX_KEY_F3 none SDLK_F3
+BX_KEY_F4 none SDLK_F4
+BX_KEY_F5 none SDLK_F5
+BX_KEY_F6 none SDLK_F6
+BX_KEY_F7 none SDLK_F7
+BX_KEY_F8 none SDLK_F8
+BX_KEY_F9 none SDLK_F9
+BX_KEY_F10 none SDLK_F10
+BX_KEY_F11 none SDLK_F11
+BX_KEY_F12 none SDLK_F12
+BX_KEY_ALT_L none SDLK_LALT
+BX_KEY_ALT_L none SDLK_LMETA
+BX_KEY_ALT_R none SDLK_MODE
+#BX_KEY_ALT_R none SDLK_Multi_key
+BX_KEY_BACKSLASH backslash SDLK_BACKSLASH
+#BX_KEY_BACKSLASH+BX_KEY_SHIFT_L '|' SDLK_bar
+BX_KEY_BACKSPACE none SDLK_BACKSPACE
+BX_KEY_CAPS_LOCK none SDLK_CAPSLOCK
+BX_KEY_COMMA ',' SDLK_COMMA
+BX_KEY_COMMA+BX_KEY_SHIFT_L '<' SDLK_LESS
+BX_KEY_CTRL_L none SDLK_LCTRL
+BX_KEY_CTRL_R none SDLK_RCTRL
+BX_KEY_DELETE none SDLK_DELETE
+BX_KEY_DOWN none SDLK_DOWN
+BX_KEY_END none SDLK_END
+BX_KEY_ENTER return SDLK_RETURN
+BX_KEY_EQUALS '=' SDLK_EQUALS
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '+' SDLK_PLUS
+BX_KEY_ESC none SDLK_ESCAPE
+#BX_KEY_GRAVE+BX_KEY_SHIFT_L '~' SDLK_asciitilde
+BX_KEY_GRAVE '`' SDLK_BACKQUOTE
+BX_KEY_HOME none SDLK_HOME
+BX_KEY_INSERT none SDLK_INSERT
+BX_KEY_KP_5 none SDLK_KP5
+#BX_KEY_KP_5 none SDLK_KP_BEGIN
+BX_KEY_KP_ADD none SDLK_KP_PLUS
+BX_KEY_KP_DELETE none SDLK_KP_PERIOD
+#BX_KEY_KP_DELETE none SDLK_KP_DELETE
+BX_KEY_KP_DIVIDE none SDLK_KP_DIVIDE
+BX_KEY_KP_DOWN none SDLK_KP2
+#BX_KEY_KP_DOWN none SDLK_KP_DOWN
+BX_KEY_KP_END none SDLK_KP1
+#BX_KEY_KP_END none SDLK_KP_END
+BX_KEY_KP_ENTER none SDLK_KP_ENTER
+BX_KEY_KP_HOME none SDLK_KP7
+#BX_KEY_KP_HOME none SDLK_KP_HOME
+BX_KEY_KP_INSERT none SDLK_KP0
+#BX_KEY_KP_INSERT none SDLK_KP_INSERT
+BX_KEY_KP_LEFT none SDLK_KP4
+#BX_KEY_KP_LEFT none SDLK_KP_LEFT
+BX_KEY_KP_MULTIPLY none SDLK_KP_MULTIPLY
+BX_KEY_KP_PAGE_DOWN none SDLK_KP3
+#BX_KEY_KP_PAGE_DOWN none SDLK_KP_PAGE_DOWN
+BX_KEY_KP_PAGE_UP none SDLK_KP9
+#BX_KEY_KP_PAGE_UP none SDLK_KP_PAGE_UP
+BX_KEY_KP_RIGHT none SDLK_KP6
+#BX_KEY_KP_RIGHT none SDLK_KP_Right
+BX_KEY_KP_SUBTRACT none SDLK_KP_MINUS
+BX_KEY_KP_UP none SDLK_KP8
+#BX_KEY_KP_UP none SDLK_KP_Up
+BX_KEY_LEFT none SDLK_LEFT
+#BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L '{' SDLK_BRACELEFT
+BX_KEY_LEFT_BRACKET '[' SDLK_LEFTBRACKET
+BX_KEY_MENU none SDLK_MENU
+BX_KEY_MINUS '-' SDLK_MINUS
+BX_KEY_MINUS+BX_KEY_SHIFT_L '_' SDLK_UNDERSCORE
+BX_KEY_NUM_LOCK none SDLK_NUMLOCK
+BX_KEY_PAGE_DOWN none SDLK_PAGEDOWN
+BX_KEY_PAGE_UP none SDLK_PAGEUP
+BX_KEY_PAUSE none SDLK_BREAK
+BX_KEY_PAUSE none SDLK_PAUSE
+BX_KEY_PERIOD+BX_KEY_SHIFT_L '>' SDLK_GREATER
+BX_KEY_PERIOD '.' SDLK_PERIOD
+BX_KEY_PRINT none SDLK_PRINT
+BX_KEY_PRINT none SDLK_SYSREQ
+BX_KEY_RIGHT none SDLK_RIGHT
+#BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '}' SDLK_BRACERIGHT
+BX_KEY_RIGHT_BRACKET ']' SDLK_RIGHTBRACKET
+BX_KEY_SCRL_LOCK none SDLK_SCROLLOCK
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L ':' SDLK_COLON
+BX_KEY_SEMICOLON ';' SDLK_SEMICOLON
+BX_KEY_SHIFT_L none SDLK_LSHIFT
+BX_KEY_SHIFT_R none SDLK_RSHIFT
+BX_KEY_SINGLE_QUOTE apostrophe SDLK_QUOTE
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L '"' SDLK_QUOTEDBL
+BX_KEY_SLASH+BX_KEY_SHIFT_L '?' SDLK_QUESTION
+BX_KEY_SLASH '/' SDLK_SLASH
+BX_KEY_SPACE space SDLK_SPACE
+#BX_KEY_TAB none SDLK_ISO_LEFT_TAB
+BX_KEY_TAB tab SDLK_TAB
+BX_KEY_UP none SDLK_UP
+BX_KEY_WIN_L none SDLK_LSUPER
+BX_KEY_WIN_R none SDLK_LSUPER
diff --git a/tools/ioemu/gui/keymaps/x11-pc-be.map b/tools/ioemu/gui/keymaps/x11-pc-be.map
new file mode 100644
index 0000000000..0a607e00c5
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-be.map
@@ -0,0 +1,220 @@
+# Bochs Keymap file
+# $Id: x11-pc-be.map,v 1.2 2003/07/29 13:31:11 bdenney Exp $
+# Target: PC(x86) keyboard, BE keymap
+# Author: Wouter Verhelst,
+# based on FR keymap by Christophe Bothamy, Bryce Denney
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+
+BX_KEY_0+BX_KEY_SHIFT_L '0' XK_0
+BX_KEY_0 'à' XK_agrave
+BX_KEY_0+BX_KEY_ALT_R '}' XK_braceright
+BX_KEY_1+BX_KEY_SHIFT_L '1' XK_1
+BX_KEY_1 '&' XK_ampersand
+BX_KEY_1+BX_KEY_ALT_R '|' XK_bar
+BX_KEY_2+BX_KEY_SHIFT_L '2' XK_2
+BX_KEY_2+BX_KEY_ALT_R '@' XK_at
+BX_KEY_2 'é' XK_eacute
+BX_KEY_3+BX_KEY_SHIFT_L '3' XK_3
+BX_KEY_3+BX_KEY_ALT_R '#' XK_numbersign
+BX_KEY_3 '"' XK_quotedbl
+BX_KEY_4+BX_KEY_SHIFT_L '4' XK_4
+BX_KEY_4 apostrophe XK_apostrophe
+BX_KEY_5+BX_KEY_SHIFT_L '5' XK_5
+BX_KEY_5 '(' XK_parenleft
+BX_KEY_6+BX_KEY_SHIFT_L '6' XK_6
+BX_KEY_6+BX_KEY_ALT_R '^' XK_asciicircum
+BX_KEY_6 '§' XK_section
+BX_KEY_7+BX_KEY_SHIFT_L '7' XK_7
+BX_KEY_7 'è' XK_egrave
+BX_KEY_8+BX_KEY_SHIFT_L '8' XK_8
+BX_KEY_8 '!' XK_exclam
+BX_KEY_9+BX_KEY_SHIFT_L '9' XK_9
+BX_KEY_9+BX_KEY_ALT_R '{' XK_braceleft
+BX_KEY_9 'ç' XK_ccedilla
+BX_KEY_A+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_A 'q' XK_q
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_B 'b' XK_b
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C 'c' XK_c
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D 'd' XK_d
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E 'e' XK_e
+BX_KEY_E+BX_KEY_ALT_R none XK_EuroSign
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_F 'f' XK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_G 'g' XK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_H 'h' XK_h
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_I 'i' XK_i
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_J 'j' XK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_K 'k' XK_k
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_L 'l' XK_l
+BX_KEY_M+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_M ',' XK_comma
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_N 'n' XK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_O 'o' XK_o
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_P 'p' XK_p
+BX_KEY_Q+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_Q 'a' XK_a
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R 'r' XK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S 's' XK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T 't' XK_t
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_U 'u' XK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V 'v' XK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_W 'z' XK_z
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X 'x' XK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_Y 'y' XK_y
+BX_KEY_Z+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_Z 'w' XK_w
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_BACKSLASH 'µ' XK_mu
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L '£' XK_sterling
+BX_KEY_BACKSLASH+BX_KEY_ALT_R '`' XK_dead_grave
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA+BX_KEY_SHIFT_L '.' XK_period
+BX_KEY_COMMA ';' XK_semicolon
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER return XK_Return
+BX_KEY_EQUALS '-' XK_minus
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '_' XK_underscore
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE '²' XK_twosuperior
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '³' XK_threesuperior
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_LEFT_BACKSLASH '<' XK_less
+BX_KEY_LEFT_BACKSLASH+BX_KEY_ALT_R backslash XK_backslash
+BX_KEY_LEFT_BRACKET none XK_dead_circumflex
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L none XK_dead_diaeresis
+BX_KEY_LEFT_BRACKET+BX_KEY_ALT_R '[' XK_bracketleft
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS+BX_KEY_SHIFT_L '°' XK_degree
+BX_KEY_MINUS ')' XK_parenright
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD ':' XK_colon
+BX_KEY_PERIOD+BX_KEY_SHIFT_L '/' XK_slash
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET '$' XK_dollar
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '*' XK_asterisk
+BX_KEY_RIGHT_BRACKET+BX_KEY_ALT_R ']' XK_bracketright
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_SEMICOLON 'm' XK_m
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_SINGLE_QUOTE+BX_KEY_ALT_R none XK_dead_acute
+BX_KEY_SINGLE_QUOTE 'ù' XK_ugrave
+BX_KEY_SLASH '=' XK_equal
+BX_KEY_SLASH+BX_KEY_SHIFT_L '+' XK_plus
+BX_KEY_SLASH+BX_KEY_ALT_R none XK_dead_tilde
+BX_KEY_SPACE space XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB tab XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/keymaps/x11-pc-da.map b/tools/ioemu/gui/keymaps/x11-pc-da.map
new file mode 100644
index 0000000000..41ead2b63d
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-da.map
@@ -0,0 +1,247 @@
+# Bochs Keymap file
+# $Id: x11-pc-da.map,v 0.9 2002/09/02
+# Target: PC(x86) keyboard, DA keymap
+# Author: Andreas Ott
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+
+BX_KEY_0 '0' XK_0
+BX_KEY_0+BX_KEY_ALT_R '}' XK_braceright
+BX_KEY_0+BX_KEY_SHIFT_L '=' XK_equal
+BX_KEY_1 '1' XK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' XK_exclam
+BX_KEY_1+BX_KEY_ALT_R '¡' XK_exclamdown
+BX_KEY_2 '2' XK_2
+BX_KEY_2+BX_KEY_SHIFT_L '=' XK_quotedbl
+BX_KEY_2+BX_KEY_ALT_R '@' XK_at # XK_twosuperior
+BX_KEY_3 '3' XK_3
+BX_KEY_3+BX_KEY_SHIFT_L '#' XK_numbersign
+BX_KEY_3+BX_KEY_ALT_R '£' XK_sterling
+BX_KEY_4 '4' XK_4
+BX_KEY_4+BX_KEY_SHIFT_L '¤' XK_currency
+BX_KEY_4+BX_KEY_ALT_R '$' XK_dollar
+BX_KEY_5 '5' XK_5
+BX_KEY_5+BX_KEY_ALT_R '½' XK_onehalf
+BX_KEY_5+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_6 '6' XK_6
+BX_KEY_6+BX_KEY_SHIFT_L '&' XK_ampersand
+BX_KEY_6+BX_KEY_ALT_R '¥' XK_yen
+BX_KEY_7 '7' XK_7
+BX_KEY_7+BX_KEY_ALT_R '{' XK_braceleft
+BX_KEY_7+BX_KEY_SHIFT_L '/' XK_slash
+BX_KEY_8 '8' XK_8
+BX_KEY_8+BX_KEY_ALT_R '[' XK_bracketleft
+BX_KEY_8+BX_KEY_SHIFT_L '(' XK_parenleft
+BX_KEY_9 '9' XK_9
+BX_KEY_9+BX_KEY_ALT_R ']' XK_bracketright
+BX_KEY_9+BX_KEY_SHIFT_L ')' XK_parenright
+BX_KEY_A+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_A 'a' XK_a
+BX_KEY_A+BX_KEY_ALT_R 'ª' XK_ordfeminine
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_B 'b' XK_b
+BX_KEY_B+BX_KEY_ALT_R none XK_rightdoublequotemark
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C 'c' XK_c
+BX_KEY_C+BX_KEY_ALT_R '©' XK_copyright
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D 'd' XK_d
+BX_KEY_D+BX_KEY_ALT_R 'ð' XK_eth
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E+BX_KEY_ALT_R '?' XK_EuroSign
+BX_KEY_E 'e' XK_e
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_F+BX_KEY_ALT_R '?' XK_dstroke
+BX_KEY_F 'f' XK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_G+BX_KEY_ALT_R '?' XK_eng
+BX_KEY_G 'g' XK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_H 'h' XK_h
+BX_KEY_H+BX_KEY_ALT_R '?' XK_hstroke
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_I 'i' XK_i
+BX_KEY_I+BX_KEY_ALT_R none XK_rightarrow
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_J 'j' XK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_K 'k' XK_k
+BX_KEY_K+BX_KEY_ALT_R none XK_kra
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_L 'l' XK_l
+BX_KEY_M+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_M 'm' XK_m
+BX_KEY_M+BX_KEY_ALT_R 'µ' XK_mu
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_N 'n' XK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_O 'o' XK_o
+BX_KEY_O+BX_KEY_ALT_R none XK_oslash
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_P 'p' XK_p
+BX_KEY_P+BX_KEY_ALT_R 'þ' XK_thorn
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_Q+BX_KEY_ALT_R '@' XK_at
+BX_KEY_Q 'q' XK_q
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R+BX_KEY_ALT_R '®' XK_registered
+BX_KEY_R 'r' XK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S 's' XK_s
+BX_KEY_S+BX_KEY_ALT_R 'ß' XK_ssharp
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T 't' XK_t
+BX_KEY_T+BX_KEY_ALT_R 'þ' XK_thorn
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_U+BX_KEY_ALT_R none XK_downarrow
+BX_KEY_U 'u' XK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V+BX_KEY_ALT_R none XK_leftdoublequotemark
+BX_KEY_V 'v' XK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_W+BX_KEY_ALT_R '?' XK_lstroke
+BX_KEY_W 'w' XK_w
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X+BX_KEY_ALT_R '»' XK_guillemotright
+BX_KEY_X 'x' XK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_Y+BX_KEY_ALT_R none XK_leftarrow
+BX_KEY_Y 'y' XK_y
+BX_KEY_Z+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_Z+BX_KEY_ALT_R '«' XK_guillemotleft
+BX_KEY_Z 'z' XK_z
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_BACKSLASH apostrophe XK_apostrophe
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L '*' XK_asterisk
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA ',' XK_comma
+BX_KEY_COMMA+BX_KEY_ALT_R none XK_horizconnector
+BX_KEY_COMMA+BX_KEY_SHIFT_L ';' XK_semicolon
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER return XK_Return
+BX_KEY_EQUALS none XK_acute
+BX_KEY_EQUALS+BX_KEY_ALT_R '|' XK_bar
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '`' XK_grave
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE '½' XK_onehalf
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '§' XK_section
+BX_KEY_GRAVE+BX_KEY_ALT_R '¾' XK_threequarters
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BACKSLASH+BX_KEY_ALT_R backslash XK_backslash
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_LEFT_BACKSLASH '<' XK_less
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L 'Å' XK_Aring
+BX_KEY_LEFT_BRACKET+BX_KEY_ALT_L none XK_diaeresis
+BX_KEY_LEFT_BRACKET 'å' XK_aring
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS+BX_KEY_ALT_R '±' XK_plusminus
+BX_KEY_MINUS+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_MINUS '+' XK_plus
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD+BX_KEY_SHIFT_L ':' XK_colon
+BX_KEY_PERIOD '.' XK_period
+BX_KEY_PERIOD+BX_KEY_ALT_R '·' XK_periodcentered
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET+BX_KEY_ALT_R '~' XK_asciitilde
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '^' XK_asciicircum
+BX_KEY_RIGHT_BRACKET '"' XK_diaeresis
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L 'Æ' XK_AE
+BX_KEY_SEMICOLON 'æ' XK_ae
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE 'ø' XK_oslash
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L 'Ø' XK_Ooblique
+BX_KEY_SLASH+BX_KEY_ALT_R '­' XK_hyphen
+BX_KEY_SLASH '-' XK_minus
+BX_KEY_SLASH+BX_KEY_SHIFT_L '_' XK_underscore
+BX_KEY_SPACE space XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB tab XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/keymaps/x11-pc-de.map b/tools/ioemu/gui/keymaps/x11-pc-de.map
new file mode 100644
index 0000000000..929ad0625e
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-de.map
@@ -0,0 +1,247 @@
+# Bochs Keymap file
+# $Id: x11-pc-de.map,v 1.7 2002/10/24 21:06:56 bdenney Exp $
+# Target: PC(x86) keyboard, DE keymap
+# Author: Volker Ruppert
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+
+BX_KEY_0 '0' XK_0
+BX_KEY_0+BX_KEY_ALT_R '}' XK_braceright
+BX_KEY_0+BX_KEY_SHIFT_L '=' XK_equal
+BX_KEY_1 '1' XK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' XK_exclam
+BX_KEY_1+BX_KEY_ALT_R '¹' XK_onesuperior
+BX_KEY_2 '2' XK_2
+BX_KEY_2+BX_KEY_SHIFT_L '"' XK_quotedbl
+BX_KEY_2+BX_KEY_ALT_R '²' XK_twosuperior
+BX_KEY_3 '3' XK_3
+BX_KEY_3+BX_KEY_SHIFT_L '§' XK_section
+BX_KEY_3+BX_KEY_ALT_R '³' XK_threesuperior
+BX_KEY_4 '4' XK_4
+BX_KEY_4+BX_KEY_SHIFT_L '$' XK_dollar
+BX_KEY_4+BX_KEY_ALT_R '¼' XK_onequarter
+BX_KEY_5 '5' XK_5
+BX_KEY_5+BX_KEY_ALT_R '½' XK_onehalf
+BX_KEY_5+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_6 '6' XK_6
+BX_KEY_6+BX_KEY_SHIFT_L '&' XK_ampersand
+BX_KEY_6+BX_KEY_ALT_R '¾' XK_threequarters
+BX_KEY_7 '7' XK_7
+BX_KEY_7+BX_KEY_ALT_R '{' XK_braceleft
+BX_KEY_7+BX_KEY_SHIFT_L '/' XK_slash
+BX_KEY_8 '8' XK_8
+BX_KEY_8+BX_KEY_ALT_R '[' XK_bracketleft
+BX_KEY_8+BX_KEY_SHIFT_L '(' XK_parenleft
+BX_KEY_9 '9' XK_9
+BX_KEY_9+BX_KEY_ALT_R ']' XK_bracketright
+BX_KEY_9+BX_KEY_SHIFT_L ')' XK_parenright
+BX_KEY_A+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_A 'a' XK_a
+BX_KEY_A+BX_KEY_ALT_R 'æ' XK_ae
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_B 'b' XK_b
+BX_KEY_B+BX_KEY_ALT_R none XK_rightdoublequotemark
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C 'c' XK_c
+BX_KEY_C+BX_KEY_ALT_R '¢' XK_cent
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D 'd' XK_d
+BX_KEY_D+BX_KEY_ALT_R 'ð' XK_eth
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E+BX_KEY_ALT_R none XK_EuroSign
+BX_KEY_E 'e' XK_e
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_F+BX_KEY_ALT_R none XK_dstroke
+BX_KEY_F 'f' XK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_G+BX_KEY_ALT_R none XK_eng
+BX_KEY_G 'g' XK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_H 'h' XK_h
+BX_KEY_H+BX_KEY_ALT_R none XK_hstroke
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_I 'i' XK_i
+BX_KEY_I+BX_KEY_ALT_R none XK_rightarrow
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_J 'j' XK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_K 'k' XK_k
+BX_KEY_K+BX_KEY_ALT_R none XK_kra
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_L 'l' XK_l
+BX_KEY_M+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_M 'm' XK_m
+BX_KEY_M+BX_KEY_ALT_R 'µ' XK_mu
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_N 'n' XK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_O 'o' XK_o
+BX_KEY_O+BX_KEY_ALT_R 'ø' XK_oslash
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_P 'p' XK_p
+BX_KEY_P+BX_KEY_ALT_R 'þ' XK_thorn
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_Q+BX_KEY_ALT_R '@' XK_at
+BX_KEY_Q 'q' XK_q
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R+BX_KEY_ALT_R '¶' XK_paragraph
+BX_KEY_R 'r' XK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S 's' XK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T 't' XK_t
+BX_KEY_T+BX_KEY_ALT_R none XK_tslash
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_U+BX_KEY_ALT_R none XK_downarrow
+BX_KEY_U 'u' XK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V+BX_KEY_ALT_R none XK_leftdoublequotemark
+BX_KEY_V 'v' XK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_W+BX_KEY_ALT_R none XK_lstroke
+BX_KEY_W 'w' XK_w
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X+BX_KEY_ALT_R '»' XK_guillemotright
+BX_KEY_X 'x' XK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_Y+BX_KEY_ALT_R none XK_leftarrow
+BX_KEY_Y 'z' XK_z
+BX_KEY_Z+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_Z+BX_KEY_ALT_R '«' XK_guillemotleft
+BX_KEY_Z 'y' XK_y
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_BACKSLASH apostrophe XK_apostrophe
+BX_KEY_BACKSLASH '#' XK_numbersign
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA ',' XK_comma
+BX_KEY_COMMA+BX_KEY_ALT_R none XK_horizconnector
+BX_KEY_COMMA+BX_KEY_SHIFT_L ';' XK_semicolon
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER return XK_Return
+BX_KEY_EQUALS none XK_acute
+BX_KEY_EQUALS+BX_KEY_ALT_R '¸' XK_cedilla
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '`' XK_grave
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE '^' XK_asciicircum
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '°' XK_degree
+BX_KEY_GRAVE+BX_KEY_ALT_R '¬' XK_notsign
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BACKSLASH+BX_KEY_ALT_R '|' XK_bar
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_LEFT_BACKSLASH '<' XK_less
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L 'Ü' XK_Udiaeresis
+BX_KEY_LEFT_BRACKET+BX_KEY_ALT_L none XK_diaeresis
+BX_KEY_LEFT_BRACKET 'ü' XK_udiaeresis
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS+BX_KEY_ALT_L backslash XK_backslash
+BX_KEY_MINUS+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_MINUS 'ß' XK_ssharp
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD+BX_KEY_SHIFT_L ':' XK_colon
+BX_KEY_PERIOD '.' XK_period
+BX_KEY_PERIOD+BX_KEY_ALT_L '·' XK_periodcentered
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET+BX_KEY_ALT_R '~' XK_asciitilde
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '*' XK_asterisk
+BX_KEY_RIGHT_BRACKET '+' XK_plus
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L 'Ö' XK_Odiaeresis
+BX_KEY_SEMICOLON 'ö' XK_odiaeresis
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L 'Ä' XK_Adiaeresis
+BX_KEY_SINGLE_QUOTE 'ä' XK_adiaeresis
+BX_KEY_SLASH none XK_dead_belowdot
+BX_KEY_SLASH '-' XK_minus
+BX_KEY_SLASH+BX_KEY_SHIFT_L '_' XK_underscore
+BX_KEY_SPACE space XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB tab XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/keymaps/x11-pc-es.map b/tools/ioemu/gui/keymaps/x11-pc-es.map
new file mode 100644
index 0000000000..3856044ac0
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-es.map
@@ -0,0 +1,217 @@
+# Bochs Keymap file
+# $Id: x11-pc-es.map,v 1.4 2002/09/25 08:00:24 bdenney Exp $
+# Target: PC(x86) keyboard, ES keymap
+# Author: Vicente Hernando Ara
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+#
+BX_KEY_0 none XK_0
+BX_KEY_0 none XK_equal
+BX_KEY_1 none XK_1
+BX_KEY_1 none XK_bar
+BX_KEY_1 none XK_exclam
+BX_KEY_2 none XK_2
+BX_KEY_2 none XK_at
+BX_KEY_2 none XK_quotedbl
+BX_KEY_3 none XK_3
+BX_KEY_3 none XK_numbersign
+BX_KEY_3 none XK_periodcentered
+BX_KEY_4 none XK_4
+BX_KEY_4 none XK_dollar
+BX_KEY_5 none XK_5
+BX_KEY_5 none XK_percent
+BX_KEY_6 none XK_6
+BX_KEY_6 none XK_ampersand
+BX_KEY_7 none XK_7
+BX_KEY_7 none XK_slash
+BX_KEY_8 none XK_8
+BX_KEY_8 none XK_parenleft
+BX_KEY_9 none XK_9
+BX_KEY_9 none XK_parenright
+BX_KEY_A none XK_A
+BX_KEY_A none XK_a
+BX_KEY_B none XK_B
+BX_KEY_B none XK_b
+BX_KEY_C none XK_C
+BX_KEY_C none XK_c
+BX_KEY_D none XK_D
+BX_KEY_D none XK_d
+BX_KEY_E none XK_E
+BX_KEY_E none XK_EuroSign
+BX_KEY_E none XK_e
+BX_KEY_F none XK_F
+BX_KEY_F none XK_f
+BX_KEY_G none XK_G
+BX_KEY_G none XK_g
+BX_KEY_H none XK_H
+BX_KEY_H none XK_h
+BX_KEY_I none XK_I
+BX_KEY_I none XK_i
+BX_KEY_J none XK_J
+BX_KEY_J none XK_j
+BX_KEY_K none XK_K
+BX_KEY_K none XK_k
+BX_KEY_L none XK_L
+BX_KEY_L none XK_l
+BX_KEY_M none XK_M
+BX_KEY_M none XK_m
+BX_KEY_N none XK_N
+BX_KEY_N none XK_n
+BX_KEY_O none XK_O
+BX_KEY_O none XK_o
+BX_KEY_P none XK_P
+BX_KEY_P none XK_p
+BX_KEY_Q none XK_Q
+BX_KEY_Q none XK_q
+BX_KEY_R none XK_R
+BX_KEY_R none XK_r
+BX_KEY_S none XK_S
+BX_KEY_S none XK_s
+BX_KEY_T none XK_T
+BX_KEY_T none XK_t
+BX_KEY_U none XK_U
+BX_KEY_U none XK_u
+BX_KEY_V none XK_V
+BX_KEY_V none XK_v
+BX_KEY_W none XK_W
+BX_KEY_W none XK_w
+BX_KEY_X none XK_X
+BX_KEY_X none XK_x
+BX_KEY_Y none XK_Y
+BX_KEY_Y none XK_y
+BX_KEY_Z none XK_Z
+BX_KEY_Z none XK_z
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_BACKSLASH none XK_Ccedilla
+BX_KEY_BACKSLASH none XK_ccedilla
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA none XK_comma
+BX_KEY_COMMA none XK_semicolon
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER none XK_Return
+BX_KEY_EQUALS none XK_exclamdown
+BX_KEY_EQUALS none XK_questiondown
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE none XK_asciitilde
+BX_KEY_GRAVE none XK_backslash
+BX_KEY_GRAVE none XK_grave
+BX_KEY_GRAVE none XK_masculine
+BX_KEY_GRAVE none XK_ordfeminine
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BACKSLASH none XK_greater
+BX_KEY_LEFT_BACKSLASH none XK_less
+BX_KEY_LEFT_BRACKET none XK_braceleft
+BX_KEY_LEFT_BRACKET none XK_bracketleft
+BX_KEY_LEFT_BRACKET none XK_dead_circumflex
+BX_KEY_LEFT_BRACKET none XK_dead_grave
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS none XK_apostrophe
+BX_KEY_MINUS none XK_question
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD none XK_colon
+BX_KEY_PERIOD none XK_period
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET none XK_asterisk
+BX_KEY_RIGHT_BRACKET none XK_braceright
+BX_KEY_RIGHT_BRACKET none XK_bracketright
+BX_KEY_RIGHT_BRACKET none XK_plus
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON none XK_Ntilde
+BX_KEY_SEMICOLON none XK_ntilde
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE none XK_dead_acute
+BX_KEY_SINGLE_QUOTE none XK_dead_diaeresis
+BX_KEY_SLASH none XK_minus
+BX_KEY_SLASH none XK_underscore
+BX_KEY_SPACE none XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB none XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/keymaps/x11-pc-fr.map b/tools/ioemu/gui/keymaps/x11-pc-fr.map
new file mode 100644
index 0000000000..869b328552
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-fr.map
@@ -0,0 +1,218 @@
+# Bochs Keymap file
+# $Id: x11-pc-fr.map,v 1.5 2002/09/25 08:00:24 bdenney Exp $
+# Target: PC(x86) keyboard, FR keymap
+# Author: Christophe Bothamy, Bryce Denney
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+
+BX_KEY_0+BX_KEY_SHIFT_L '0' XK_0
+BX_KEY_0 'à' XK_agrave
+BX_KEY_0+BX_KEY_ALT_R '@' XK_at
+BX_KEY_1+BX_KEY_SHIFT_L '1' XK_1
+BX_KEY_1 '&' XK_ampersand
+BX_KEY_2+BX_KEY_SHIFT_L '2' XK_2
+BX_KEY_2+BX_KEY_ALT_R '~' XK_asciitilde
+BX_KEY_2 'é' XK_eacute
+BX_KEY_3+BX_KEY_SHIFT_L '3' XK_3
+BX_KEY_3+BX_KEY_ALT_R '#' XK_numbersign
+BX_KEY_3 '"' XK_quotedbl
+BX_KEY_4+BX_KEY_SHIFT_L '4' XK_4
+BX_KEY_4 apostrophe XK_apostrophe
+BX_KEY_4+BX_KEY_ALT_R '{' XK_braceleft
+BX_KEY_5+BX_KEY_SHIFT_L '5' XK_5
+BX_KEY_5+BX_KEY_ALT_R '[' XK_bracketleft
+BX_KEY_5 '(' XK_parenleft
+BX_KEY_6+BX_KEY_SHIFT_L '6' XK_6
+BX_KEY_6+BX_KEY_ALT_R '|' XK_bar
+BX_KEY_6 '-' XK_minus
+BX_KEY_7+BX_KEY_SHIFT_L '7' XK_7
+BX_KEY_7 'è' XK_egrave
+BX_KEY_7+BX_KEY_ALT_R '`' XK_grave
+BX_KEY_8+BX_KEY_SHIFT_L '8' XK_8
+BX_KEY_8+BX_KEY_ALT_R backslash XK_backslash
+BX_KEY_8 '_' XK_underscore
+BX_KEY_9+BX_KEY_SHIFT_L '9' XK_9
+BX_KEY_9+BX_KEY_ALT_R '^' XK_asciicircum
+BX_KEY_9 'ç' XK_ccedilla
+BX_KEY_A+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_A 'q' XK_q
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_B 'b' XK_b
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C 'c' XK_c
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D 'd' XK_d
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E 'e' XK_e
+BX_KEY_E+BX_KEY_ALT_R none XK_EuroSign
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_F 'f' XK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_G 'g' XK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_H 'h' XK_h
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_I 'i' XK_i
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_J 'j' XK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_K 'k' XK_k
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_L 'l' XK_l
+BX_KEY_M+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_M ',' XK_comma
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_N 'n' XK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_O 'o' XK_o
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_P 'p' XK_p
+BX_KEY_Q+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_Q 'a' XK_a
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R 'r' XK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S 's' XK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T 't' XK_t
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_U 'u' XK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V 'v' XK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_W 'z' XK_z
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X 'x' XK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_Y 'y' XK_y
+BX_KEY_Z+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_Z 'w' XK_w
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_BACKSLASH '*' XK_asterisk
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L 'µ' XK_mu
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA+BX_KEY_SHIFT_L '.' XK_period
+BX_KEY_COMMA ';' XK_semicolon
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER return XK_Return
+BX_KEY_EQUALS+BX_KEY_ALT_R '}' XK_braceright
+BX_KEY_EQUALS '=' XK_equal
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '+' XK_plus
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE '²' XK_twosuperior
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_LEFT_BACKSLASH '<' XK_less
+BX_KEY_LEFT_BRACKET '^' XK_dead_circumflex
+BX_KEY_LEFT_BRACKET none XK_dead_diaeresis
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS+BX_KEY_ALT_R ']' XK_bracketright
+BX_KEY_MINUS+BX_KEY_SHIFT_L '°' XK_degree
+BX_KEY_MINUS ')' XK_parenright
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD ':' XK_colon
+BX_KEY_PERIOD+BX_KEY_SHIFT_L '/' XK_slash
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET+BX_KEY_ALT_R '¤' XK_currency
+BX_KEY_RIGHT_BRACKET '$' XK_dollar
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '£' XK_sterling
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_SEMICOLON 'm' XK_m
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_SINGLE_QUOTE 'ù' XK_ugrave
+BX_KEY_SLASH '!' XK_exclam
+BX_KEY_SLASH+BX_KEY_SHIFT_L '§' XK_section
+BX_KEY_SPACE space XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB tab XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/keymaps/x11-pc-it.map b/tools/ioemu/gui/keymaps/x11-pc-it.map
new file mode 100644
index 0000000000..fac365e0ba
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-it.map
@@ -0,0 +1,207 @@
+# Bochs Keymap file
+# $Id: x11-pc-it.map,v 1.2 2002/09/25 08:00:25 bdenney Exp $
+# Target: PC(x86) keyboard, IT keymap
+# Author: Emanuele Goldoni
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+#
+
+BX_KEY_0 '0' XK_0
+BX_KEY_0+BX_KEY_SHIFT_L '=' XK_equal
+BX_KEY_1 '1' XK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' XK_exclam
+BX_KEY_2 '2' XK_2
+BX_KEY_2+BX_KEY_SHIFT_L '"' XK_quotedbl
+BX_KEY_3 '3' XK_3
+BX_KEY_3+BX_KEY_SHIFT_L '£' XK_sterling
+BX_KEY_4 '4' XK_4
+BX_KEY_4+BX_KEY_SHIFT_L '$' XK_dollar
+BX_KEY_5 '5' XK_5
+BX_KEY_5+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_6 '6' XK_6
+BX_KEY_6+BX_KEY_SHIFT_L '&' XK_ampersand
+BX_KEY_7 '7' XK_7
+BX_KEY_7+BX_KEY_SHIFT_L '/' XK_slash
+BX_KEY_8 '8' XK_8
+BX_KEY_8+BX_KEY_SHIFT_L '(' XK_parenleft
+BX_KEY_9 '9' XK_9
+BX_KEY_9+BX_KEY_SHIFT_L ')' XK_parenright
+BX_KEY_A+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_A 'a' XK_a
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_B 'b' XK_b
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C 'c' XK_c
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D 'd' XK_d
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E 'e' XK_e
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_F 'f' XK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_G 'g' XK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_H 'h' XK_h
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_I 'i' XK_i
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_J 'j' XK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_K 'k' XK_k
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_L 'l' XK_l
+BX_KEY_M+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_M 'm' XK_m
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_N 'n' XK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_O 'o' XK_o
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_P 'p' XK_p
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_Q 'q' XK_q
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R 'r' XK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S 's' XK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T 't' XK_t
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_U 'u' XK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V 'v' XK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_W 'w' XK_w
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X 'x' XK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_Y 'y' XK_y
+BX_KEY_Z+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_Z 'z' XK_z
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_BACKSLASH 'ù' XK_ugrave
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L '§' XK_section
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_LEFT_BACKSLASH '<' XK_less
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA ',' XK_comma
+BX_KEY_COMMA+BX_KEY_SHIFT_L ';' XK_semicolon
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER return XK_Return
+BX_KEY_EQUALS 'ì' XK_igrave
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '^' XK_asciicircum
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '|' XK_bar
+BX_KEY_GRAVE backslash XK_backslash
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L 'é' XK_eacute
+BX_KEY_LEFT_BRACKET 'è' XK_egrave
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS apostrophe XK_apostrophe
+BX_KEY_MINUS+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD+BX_KEY_SHIFT_L ':' XK_colon
+BX_KEY_PERIOD '.' XK_period
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '*' XK_asterisk
+BX_KEY_RIGHT_BRACKET '+' XK_plus
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L 'ç' XK_ccedilla
+BX_KEY_SEMICOLON 'ò' XK_ograve
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE 'à' XK_agrave
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L '°' XK_degree
+BX_KEY_SLASH+BX_KEY_SHIFT_L '_' XK_underscore
+BX_KEY_SLASH '-' XK_minus
+BX_KEY_SPACE space XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB tab XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/keymaps/x11-pc-se.map b/tools/ioemu/gui/keymaps/x11-pc-se.map
new file mode 100644
index 0000000000..c564ca83c6
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-se.map
@@ -0,0 +1,278 @@
+# Bochs Keymap file
+# $Id: x11-pc-se.map,v 1.2 2002/09/25 08:00:25 bdenney Exp $
+# Target: PC(x86) keyboard, SE keymap
+# Author: Magnus 'Moggen' Öberg
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+#
+
+# Upper key groups
+BX_KEY_ESC none XK_Escape
+
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+
+# Main key group
+# Row 1
+BX_KEY_GRAVE '§' XK_section
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '½' XK_onehalf
+BX_KEY_GRAVE+BX_KEY_ALT_R '¶' XK_paragraph
+BX_KEY_GRAVE+BX_KEY_SHIFT_L+BX_KEY_ALT_R '¾' XK_threequarters
+BX_KEY_1 '1' XK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' XK_exclam
+BX_KEY_1+BX_KEY_ALT_R '¡' XK_exclamdown
+BX_KEY_1+BX_KEY_SHIFT_L+BX_KEY_ALT_R '¹' XK_onesuperior
+BX_KEY_2 '2' XK_2
+BX_KEY_2+BX_KEY_SHIFT_L '"' XK_quotedbl
+BX_KEY_2+BX_KEY_ALT_R '@' XK_at
+BX_KEY_2+BX_KEY_SHIFT_L+BX_KEY_ALT_R '²' XK_twosuperior
+BX_KEY_3 '3' XK_3
+BX_KEY_3+BX_KEY_SHIFT_L '#' XK_numbersign
+BX_KEY_3+BX_KEY_ALT_R '£' XK_sterling
+BX_KEY_3+BX_KEY_SHIFT_L+BX_KEY_ALT_R '³' XK_threesuperior
+BX_KEY_4 '4' XK_4
+BX_KEY_4+BX_KEY_SHIFT_L '¤' XK_currency
+BX_KEY_4+BX_KEY_ALT_R '$' XK_dollar
+BX_KEY_4+BX_KEY_SHIFT_L+BX_KEY_ALT_R '¼' XK_onequarter
+BX_KEY_5 '5' XK_5
+BX_KEY_5+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_5+BX_KEY_SHIFT_L+BX_KEY_ALT_R '¢' XK_cent
+BX_KEY_6 '6' XK_6
+BX_KEY_6+BX_KEY_SHIFT_L '&' XK_ampersand
+BX_KEY_6+BX_KEY_ALT_R '¥' XK_yen
+BX_KEY_7 '7' XK_7
+BX_KEY_7+BX_KEY_SHIFT_L '/' XK_slash
+BX_KEY_7+BX_KEY_ALT_R '{' XK_braceleft
+BX_KEY_7+BX_KEY_SHIFT_L+BX_KEY_ALT_R '÷' XK_division
+BX_KEY_8 '8' XK_8
+BX_KEY_8+BX_KEY_SHIFT_L '(' XK_parenleft
+BX_KEY_8+BX_KEY_ALT_R '[' XK_bracketleft
+BX_KEY_9 '9' XK_9
+BX_KEY_9+BX_KEY_SHIFT_L ')' XK_parenright
+BX_KEY_9+BX_KEY_ALT_R ']' XK_bracketright
+BX_KEY_0 '0' XK_0
+BX_KEY_0+BX_KEY_SHIFT_L '=' XK_equal
+BX_KEY_0+BX_KEY_ALT_R '}' XK_braceright
+BX_KEY_0+BX_KEY_SHIFT_L+BX_KEY_ALT_R '°' XK_degree
+BX_KEY_MINUS '+' XK_plus
+BX_KEY_MINUS+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_MINUS+BX_KEY_ALT_L backslash XK_backslash
+BX_KEY_MINUS+BX_KEY_SHIFT_L+BX_KEY_ALT_R '¿' XK_questiondown
+BX_KEY_EQUALS none XK_dead_acute
+BX_KEY_EQUALS+BX_KEY_SHIFT_L none XK_dead_grave
+BX_KEY_EQUALS+BX_KEY_ALT_L '±' XK_plusminus
+BX_KEY_EQUALS+BX_KEY_ALT_L+BX_KEY_ALT_R '¬' XK_notsign
+BX_KEY_BACKSPACE none XK_BackSpace
+
+# Row 2
+BX_KEY_TAB tab XK_Tab
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_Q 'q' XK_q
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_W 'w' XK_w
+BX_KEY_W+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_E 'e' XK_e
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E+BX_KEY_SHIFT_L+BX_KEY_ALT_R none XK_EuroSign
+BX_KEY_R 'r' XK_r
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R+BX_KEY_ALT_R '®' XK_registered
+BX_KEY_T 't' XK_t
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T+BX_KEY_ALT_R 'þ' XK_thorn
+BX_KEY_T+BX_KEY_SHIFT_L+BX_KEY_ALT_R 'Þ' XK_THORN
+BX_KEY_Y 'y' XK_y
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_U 'u' XK_u
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_I 'i' XK_i
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_O 'o' XK_o
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_P 'p' XK_p
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_LEFT_BRACKET 'å' XK_aring
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L 'Å' XK_Aring
+BX_KEY_RIGHT_BRACKET none XK_dead_diaeresis
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L none XK_dead_circumflex
+BX_KEY_RIGHT_BRACKET+BX_KEY_ALT_R none XK_dead_tilde
+BX_KEY_ENTER return XK_Return
+
+# Row 3
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_A 'a' XK_a
+BX_KEY_A+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_A+BX_KEY_ALT_R 'ª' XK_ordfeminine
+BX_KEY_A+BX_KEY_SHIFT_L+BX_KEY_ALT_R 'º' XK_masculine
+BX_KEY_S 's' XK_s
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S+BX_KEY_ALT_R 'ß' XK_ssharp
+BX_KEY_D 'd' XK_d
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D+BX_KEY_ALT_R 'ð' XK_eth
+BX_KEY_D+BX_KEY_SHIFT_L+BX_KEY_ALT_R 'Ð' XK_ETH
+BX_KEY_F 'f' XK_f
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_G 'g' XK_g
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_H 'h' XK_h
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_J 'j' XK_j
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_K 'k' XK_k
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_L 'l' XK_l
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_SEMICOLON 'ö' XK_odiaeresis
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L 'Ö' XK_Odiaeresis
+BX_KEY_SEMICOLON+BX_KEY_ALT_R 'ø' XK_oslash
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L+BX_KEY_ALT_R 'Ø' XK_Ooblique
+BX_KEY_SINGLE_QUOTE 'ä' XK_adiaeresis
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L 'Ä' XK_Adiaeresis
+BX_KEY_SINGLE_QUOTE+BX_KEY_ALT_R 'æ' XK_ae
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L+BX_KEY_ALT_R 'Æ' XK_AE
+BX_KEY_BACKSLASH apostrophe XK_apostrophe
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L '*' XK_asterisk
+BX_KEY_BACKSLASH+BX_KEY_ALT_R '´' XK_acute
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L+BX_KEY_ALT_R '×' XK_multiply
+
+# Row 4
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_LEFT_BACKSLASH '<' XK_less
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_LEFT_BACKSLASH+BX_KEY_ALT_R '|' XK_bar
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L+BX_KEY_ALT_R '¦' XK_brokenbar
+BX_KEY_Z 'z' XK_z
+BX_KEY_Z+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_Z+BX_KEY_ALT_R '«' XK_guillemotleft
+BX_KEY_X 'x' XK_x
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X+BX_KEY_ALT_R '»' XK_guillemotright
+BX_KEY_C 'c' XK_c
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C+BX_KEY_ALT_R '©' XK_copyright
+BX_KEY_V 'v' XK_v
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V+BX_KEY_SHIFT_L+BX_KEY_ALT_R '`' XK_grave
+BX_KEY_B 'b' XK_b
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_N 'n' XK_n
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_M 'm' XK_m
+BX_KEY_M+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_M+BX_KEY_ALT_R 'µ' XK_mu
+BX_KEY_COMMA ',' XK_comma
+BX_KEY_COMMA+BX_KEY_SHIFT_L ';' XK_semicolon
+BX_KEY_PERIOD '.' XK_period
+BX_KEY_PERIOD+BX_KEY_SHIFT_L ':' XK_colon
+BX_KEY_PERIOD+BX_KEY_ALT_R '·' XK_periodcentered
+BX_KEY_SLASH '-' XK_minus
+BX_KEY_SLASH+BX_KEY_SHIFT '_' XK_underscore
+BX_KEY_SLASH+BX_KEY_ALT_R '­' XK_hyphen
+BX_KEY_SLASH+BX_KEY_SHIFT+BX_KEY_ALT_R '­' XK_macron
+BX_KEY_SHIFT_R none XK_Shift_R
+
+# Row 5
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_SPACE space XK_space
+BX_KEY_SPACE+BX_KEY_ALT_R none XK_nobreakspace
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_WIN_R none XK_Super_R
+BX_KEY_MENU none XK_Menu
+BX_KEY_CTRL_R none XK_Control_R
+
+# Ins/Del/Home/End/PgUp/PgDn
+BX_KEY_INSERT none XK_Insert
+BX_KEY_DELETE none XK_Delete
+BX_KEY_HOME none XK_Home
+BX_KEY_END none XK_End
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAGE_DOWN none XK_Page_Down
+
+# Arrow keys
+BX_KEY_LEFT none XK_Left
+BX_KEY_RIGHT none XK_Right
+BX_KEY_UP none XK_Up
+BX_KEY_DOWN none XK_Down
+
+# Numerical keypad
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
diff --git a/tools/ioemu/gui/keymaps/x11-pc-uk.map b/tools/ioemu/gui/keymaps/x11-pc-uk.map
new file mode 100644
index 0000000000..05abbe4f83
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-uk.map
@@ -0,0 +1,209 @@
+# Bochs Keymap file
+# $Id: x11-pc-uk.map,v 1.1 2002/12/11 21:35:50 bdenney Exp $
+# Target: PC(x86) keyboard, UK keymap
+# Author: Denis Lenihan
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+#
+
+BX_KEY_0 '0' XK_0
+BX_KEY_0+BX_KEY_SHIFT_L ')' XK_parenright
+BX_KEY_1 '1' XK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' XK_exclam
+BX_KEY_2 '2' XK_2
+BX_KEY_2+BX_KEY_SHIFT_L '"' XK_quotedbl
+BX_KEY_3 '3' XK_3
+BX_KEY_3+BX_KEY_SHIFT_L '£' XK_sterling
+BX_KEY_3+BX_KEY_ALT_R '|' XK_EuroSign
+BX_KEY_4 '4' XK_4
+BX_KEY_4+BX_KEY_SHIFT_L '$' XK_dollar
+BX_KEY_5 '5' XK_5
+BX_KEY_5+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_6 '6' XK_6
+BX_KEY_6+BX_KEY_SHIFT_L '^' XK_asciicircum
+BX_KEY_7 '7' XK_7
+BX_KEY_7+BX_KEY_SHIFT_L '&' XK_ampersand
+BX_KEY_8 '8' XK_8
+BX_KEY_8+BX_KEY_SHIFT_L '*' XK_asterisk
+BX_KEY_9 '9' XK_9
+BX_KEY_9+BX_KEY_SHIFT_L '(' XK_parenleft
+BX_KEY_A+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_A 'a' XK_a
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_B 'b' XK_b
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C 'c' XK_c
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D 'd' XK_d
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E 'e' XK_e
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_F 'f' XK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_G 'g' XK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_H 'h' XK_h
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_I 'i' XK_i
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_J 'j' XK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_K 'k' XK_k
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_L 'l' XK_l
+BX_KEY_M+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_M 'm' XK_m
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_N 'n' XK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_O 'o' XK_o
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_P 'p' XK_p
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_Q 'q' XK_q
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R 'r' XK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S 's' XK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T 't' XK_t
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_U 'u' XK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V 'v' XK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_W 'w' XK_w
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X 'x' XK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_Y 'y' XK_y
+BX_KEY_Z+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_Z 'z' XK_z
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_LEFT_BACKSLASH '\' XK_backslash
+BX_KEY_LEFT_BACKSLASH+BX_KEY_SHIFT_L '|' XK_bar
+BX_KEY_BACKSLASH '~' XK_asciitilde
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L '#' XK_numbersign
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA ',' XK_comma
+BX_KEY_COMMA+BX_KEY_SHIFT_L '<' XK_less
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER return XK_Return
+BX_KEY_EQUALS '=' XK_equal
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '+' XK_plus
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '¬' XK_notsign
+BX_KEY_GRAVE '`' XK_grave
+BX_KEY_GRAVE+BX_KEY_ALT_R '|' XK_bar
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L '{' XK_braceleft
+BX_KEY_LEFT_BRACKET '[' XK_bracketleft
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS '-' XK_minus
+BX_KEY_MINUS+BX_KEY_SHIFT_L '_' XK_underscore
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_PERIOD '.' XK_period
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '}' XK_braceright
+BX_KEY_RIGHT_BRACKET ']' XK_bracketright
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L ':' XK_colon
+BX_KEY_SEMICOLON ';' XK_semicolon
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE apostrophe XK_apostrophe
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L '@' XK_at
+BX_KEY_SLASH+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_SLASH '/' XK_slash
+BX_KEY_SPACE space XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB tab XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/keymaps/x11-pc-us.map b/tools/ioemu/gui/keymaps/x11-pc-us.map
new file mode 100644
index 0000000000..8cda7a8a13
--- /dev/null
+++ b/tools/ioemu/gui/keymaps/x11-pc-us.map
@@ -0,0 +1,205 @@
+# Bochs Keymap file
+# $Id: x11-pc-us.map,v 1.3 2002/09/25 08:00:25 bdenney Exp $
+# Target: PC(x86) keyboard, US keymap
+# Author: Christophe Bothamy, Bryce Denney
+#
+# The keymap file describes the layout of a keyboard, and how it translates
+# into Bochs key codes.
+#
+# Format:
+# BX_Keysym ASCII_equivalent Xwin_Keysym
+#
+# Or, for keys that require modifiers:
+# BX_Keysym+BX_Modifier ASCII_equivalent Xwin_Keysym
+#
+# BX_Keysym and BX_Modifier must be present in the bx_key_symbol[] list in
+# gui/keymap.cc. The BX_Modifier is usually a shift key press, but it
+# could be any key. Presently a maximum of one modifier is supported, but this
+# could be changed in keymap.h (structure def has only one slot for modifier),
+# keymap.cc (parsing code), and iodev/keyboard.cc (simulate keypresses for >1
+# modifier).
+#
+# The ASCII_equivalent must be either apostrophe + one character + apostrophe,
+# or one of these keywords: space, return, tab, backslash, apostrophe, none.
+# This format is designed to look like a char constant in C, but it's a very
+# simple parser. There's no concept of backslash being an escape char. The
+# backslash and apostrophe entries are provided for aesthetic purposes only: no
+# C++ programmer wants to see '\' or '''. The parser doesn't care, but they are
+# ugly.
+#
+# Xwin_Keysym is the X windows equivalent of the key combination. These
+# codes should match whatever you find in /usr/X11R6/include/X11/keysymdef.h.
+# If you're running X windows, Bochs will take each of these Xwin_Keysyms,
+# pull off the XK_ in front, and use XStringToKeysym() to change them into
+# numerical codes. If this lookup fails, you will get a panic and you need
+# to edit the keymap file.
+#
+
+BX_KEY_0 '0' XK_0
+BX_KEY_0+BX_KEY_SHIFT_L ')' XK_parenright
+BX_KEY_1 '1' XK_1
+BX_KEY_1+BX_KEY_SHIFT_L '!' XK_exclam
+BX_KEY_2 '2' XK_2
+BX_KEY_2+BX_KEY_SHIFT_L '@' XK_at
+BX_KEY_3 '3' XK_3
+BX_KEY_3+BX_KEY_SHIFT_L '#' XK_numbersign
+BX_KEY_4 '4' XK_4
+BX_KEY_4+BX_KEY_SHIFT_L '$' XK_dollar
+BX_KEY_5 '5' XK_5
+BX_KEY_5+BX_KEY_SHIFT_L '%' XK_percent
+BX_KEY_6 '6' XK_6
+BX_KEY_6+BX_KEY_SHIFT_L '^' XK_asciicircum
+BX_KEY_7 '7' XK_7
+BX_KEY_7+BX_KEY_SHIFT_L '&' XK_ampersand
+BX_KEY_8 '8' XK_8
+BX_KEY_8+BX_KEY_SHIFT_L '*' XK_asterisk
+BX_KEY_9 '9' XK_9
+BX_KEY_9+BX_KEY_SHIFT_L '(' XK_parenleft
+BX_KEY_A+BX_KEY_SHIFT_L 'A' XK_A
+BX_KEY_A 'a' XK_a
+BX_KEY_B+BX_KEY_SHIFT_L 'B' XK_B
+BX_KEY_B 'b' XK_b
+BX_KEY_C+BX_KEY_SHIFT_L 'C' XK_C
+BX_KEY_C 'c' XK_c
+BX_KEY_D+BX_KEY_SHIFT_L 'D' XK_D
+BX_KEY_D 'd' XK_d
+BX_KEY_E+BX_KEY_SHIFT_L 'E' XK_E
+BX_KEY_E 'e' XK_e
+BX_KEY_F+BX_KEY_SHIFT_L 'F' XK_F
+BX_KEY_F 'f' XK_f
+BX_KEY_G+BX_KEY_SHIFT_L 'G' XK_G
+BX_KEY_G 'g' XK_g
+BX_KEY_H+BX_KEY_SHIFT_L 'H' XK_H
+BX_KEY_H 'h' XK_h
+BX_KEY_I+BX_KEY_SHIFT_L 'I' XK_I
+BX_KEY_I 'i' XK_i
+BX_KEY_J+BX_KEY_SHIFT_L 'J' XK_J
+BX_KEY_J 'j' XK_j
+BX_KEY_K+BX_KEY_SHIFT_L 'K' XK_K
+BX_KEY_K 'k' XK_k
+BX_KEY_L+BX_KEY_SHIFT_L 'L' XK_L
+BX_KEY_L 'l' XK_l
+BX_KEY_M+BX_KEY_SHIFT_L 'M' XK_M
+BX_KEY_M 'm' XK_m
+BX_KEY_N+BX_KEY_SHIFT_L 'N' XK_N
+BX_KEY_N 'n' XK_n
+BX_KEY_O+BX_KEY_SHIFT_L 'O' XK_O
+BX_KEY_O 'o' XK_o
+BX_KEY_P+BX_KEY_SHIFT_L 'P' XK_P
+BX_KEY_P 'p' XK_p
+BX_KEY_Q+BX_KEY_SHIFT_L 'Q' XK_Q
+BX_KEY_Q 'q' XK_q
+BX_KEY_R+BX_KEY_SHIFT_L 'R' XK_R
+BX_KEY_R 'r' XK_r
+BX_KEY_S+BX_KEY_SHIFT_L 'S' XK_S
+BX_KEY_S 's' XK_s
+BX_KEY_T+BX_KEY_SHIFT_L 'T' XK_T
+BX_KEY_T 't' XK_t
+BX_KEY_U+BX_KEY_SHIFT_L 'U' XK_U
+BX_KEY_U 'u' XK_u
+BX_KEY_V+BX_KEY_SHIFT_L 'V' XK_V
+BX_KEY_V 'v' XK_v
+BX_KEY_W+BX_KEY_SHIFT_L 'W' XK_W
+BX_KEY_W 'w' XK_w
+BX_KEY_X+BX_KEY_SHIFT_L 'X' XK_X
+BX_KEY_X 'x' XK_x
+BX_KEY_Y+BX_KEY_SHIFT_L 'Y' XK_Y
+BX_KEY_Y 'y' XK_y
+BX_KEY_Z+BX_KEY_SHIFT_L 'Z' XK_Z
+BX_KEY_Z 'z' XK_z
+BX_KEY_F1 none XK_F1
+BX_KEY_F2 none XK_F2
+BX_KEY_F3 none XK_F3
+BX_KEY_F4 none XK_F4
+BX_KEY_F5 none XK_F5
+BX_KEY_F6 none XK_F6
+BX_KEY_F7 none XK_F7
+BX_KEY_F8 none XK_F8
+BX_KEY_F9 none XK_F9
+BX_KEY_F10 none XK_F10
+BX_KEY_F11 none XK_F11
+BX_KEY_F12 none XK_F12
+BX_KEY_ALT_L none XK_Alt_L
+BX_KEY_ALT_L none XK_Meta_L
+BX_KEY_ALT_R none XK_Alt_R
+BX_KEY_ALT_R none XK_Mode_switch
+BX_KEY_ALT_R none XK_Multi_key
+BX_KEY_BACKSLASH backslash XK_backslash
+BX_KEY_BACKSLASH+BX_KEY_SHIFT_L '|' XK_bar
+BX_KEY_BACKSPACE none XK_BackSpace
+BX_KEY_CAPS_LOCK none XK_Caps_Lock
+BX_KEY_COMMA ',' XK_comma
+BX_KEY_COMMA+BX_KEY_SHIFT_L '<' XK_less
+BX_KEY_CTRL_L none XK_Control_L
+BX_KEY_CTRL_R none XK_Control_R
+BX_KEY_DELETE none XK_Delete
+BX_KEY_DOWN none XK_Down
+BX_KEY_END none XK_End
+BX_KEY_ENTER return XK_Return
+BX_KEY_EQUALS '=' XK_equal
+BX_KEY_EQUALS+BX_KEY_SHIFT_L '+' XK_plus
+BX_KEY_ESC none XK_Escape
+BX_KEY_GRAVE+BX_KEY_SHIFT_L '~' XK_asciitilde
+BX_KEY_GRAVE '`' XK_grave
+BX_KEY_HOME none XK_Home
+BX_KEY_INSERT none XK_Insert
+BX_KEY_KP_5 none XK_KP_5
+BX_KEY_KP_5 none XK_KP_Begin
+BX_KEY_KP_ADD none XK_KP_Add
+BX_KEY_KP_DELETE none XK_KP_Decimal
+BX_KEY_KP_DELETE none XK_KP_Delete
+BX_KEY_KP_DIVIDE none XK_KP_Divide
+BX_KEY_KP_DOWN none XK_KP_2
+BX_KEY_KP_DOWN none XK_KP_Down
+BX_KEY_KP_END none XK_KP_1
+BX_KEY_KP_END none XK_KP_End
+BX_KEY_KP_ENTER none XK_KP_Enter
+BX_KEY_KP_HOME none XK_KP_7
+BX_KEY_KP_HOME none XK_KP_Home
+BX_KEY_KP_INSERT none XK_KP_0
+BX_KEY_KP_INSERT none XK_KP_Insert
+BX_KEY_KP_LEFT none XK_KP_4
+BX_KEY_KP_LEFT none XK_KP_Left
+BX_KEY_KP_MULTIPLY none XK_KP_Multiply
+BX_KEY_KP_PAGE_DOWN none XK_KP_3
+BX_KEY_KP_PAGE_DOWN none XK_KP_Page_Down
+BX_KEY_KP_PAGE_UP none XK_KP_9
+BX_KEY_KP_PAGE_UP none XK_KP_Page_Up
+BX_KEY_KP_RIGHT none XK_KP_6
+BX_KEY_KP_RIGHT none XK_KP_Right
+BX_KEY_KP_SUBTRACT none XK_KP_Subtract
+BX_KEY_KP_UP none XK_KP_8
+BX_KEY_KP_UP none XK_KP_Up
+BX_KEY_LEFT none XK_Left
+BX_KEY_LEFT_BRACKET+BX_KEY_SHIFT_L '{' XK_braceleft
+BX_KEY_LEFT_BRACKET '[' XK_bracketleft
+BX_KEY_MENU none XK_Menu
+BX_KEY_MINUS '-' XK_minus
+BX_KEY_MINUS+BX_KEY_SHIFT_L '_' XK_underscore
+BX_KEY_NUM_LOCK none XK_Num_Lock
+BX_KEY_PAGE_DOWN none XK_Page_Down
+BX_KEY_PAGE_UP none XK_Page_Up
+BX_KEY_PAUSE none XK_Break
+BX_KEY_PAUSE none XK_Pause
+BX_KEY_PERIOD+BX_KEY_SHIFT_L '>' XK_greater
+BX_KEY_PERIOD '.' XK_period
+BX_KEY_PRINT none XK_Print
+BX_KEY_PRINT none XK_Sys_Req
+BX_KEY_RIGHT none XK_Right
+BX_KEY_RIGHT_BRACKET+BX_KEY_SHIFT_L '}' XK_braceright
+BX_KEY_RIGHT_BRACKET ']' XK_bracketright
+BX_KEY_SCRL_LOCK none XK_Scroll_Lock
+BX_KEY_SEMICOLON+BX_KEY_SHIFT_L ':' XK_colon
+BX_KEY_SEMICOLON ';' XK_semicolon
+BX_KEY_SHIFT_L none XK_Shift_L
+BX_KEY_SHIFT_R none XK_Shift_R
+BX_KEY_SINGLE_QUOTE apostrophe XK_apostrophe
+BX_KEY_SINGLE_QUOTE+BX_KEY_SHIFT_L '"' XK_quotedbl
+BX_KEY_SLASH+BX_KEY_SHIFT_L '?' XK_question
+BX_KEY_SLASH '/' XK_slash
+BX_KEY_SPACE space XK_space
+BX_KEY_TAB none XK_ISO_Left_Tab
+BX_KEY_TAB tab XK_Tab
+BX_KEY_UP none XK_Up
+BX_KEY_WIN_L none XK_Super_L
+BX_KEY_WIN_R none XK_Super_R
diff --git a/tools/ioemu/gui/nogui.cc b/tools/ioemu/gui/nogui.cc
new file mode 100644
index 0000000000..dd56d9d854
--- /dev/null
+++ b/tools/ioemu/gui/nogui.cc
@@ -0,0 +1,336 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: nogui.cc,v 1.21 2003/06/28 08:04:31 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#include "icon_bochs.h"
+
+class bx_nogui_gui_c : public bx_gui_c {
+public:
+ bx_nogui_gui_c (void) {}
+ DECLARE_GUI_VIRTUAL_METHODS()
+};
+
+// declare one instance of the gui object and call macro to insert the
+// plugin code
+static bx_nogui_gui_c *theGui = NULL;
+IMPLEMENT_GUI_PLUGIN_CODE(nogui)
+
+#define LOG_THIS theGui->
+
+// This file defines stubs for the GUI interface, which is a
+// place to start if you want to port bochs to a platform, for
+// which there is no support for your native GUI, or if you want to compile
+// bochs without any native GUI support (no output window or
+// keyboard input will be possible).
+// Look in 'x.cc', 'beos.cc', and 'win32.cc' for specific
+// implementations of this interface. -Kevin
+
+
+
+// ::SPECIFIC_INIT()
+//
+// Called from gui.cc, once upon program startup, to allow for the
+// specific GUI code (X11, BeOS, ...) to be initialized.
+//
+// argc, argv: not used right now, but the intention is to pass native GUI
+// specific options from the command line. (X11 options, BeOS options,...)
+//
+// tilewidth, tileheight: for optimization, graphics_tile_update() passes
+// only updated regions of the screen to the gui code to be redrawn.
+// These define the dimensions of a region (tile).
+// headerbar_y: A headerbar (toolbar) is display on the top of the
+// VGA window, showing floppy status, and other information. It
+// always assumes the width of the current VGA mode width, but
+// it's height is defined by this parameter.
+
+ void
+bx_nogui_gui_c::specific_init(int argc, char **argv, unsigned tilewidth, unsigned tileheight,
+ unsigned headerbar_y)
+{
+ put("NGUI");
+ UNUSED(argc);
+ UNUSED(argv);
+ UNUSED(tilewidth);
+ UNUSED(tileheight);
+ UNUSED(headerbar_y);
+
+ UNUSED(bochs_icon_bits); // global variable
+
+ if (bx_options.Oprivate_colormap->get ()) {
+ BX_INFO(("private_colormap option ignored."));
+ }
+}
+
+
+// ::HANDLE_EVENTS()
+//
+// Called periodically (vga_update_interval in .bochsrc) so the
+// the gui code can poll for keyboard, mouse, and other
+// relevant events.
+
+ void
+bx_nogui_gui_c::handle_events(void)
+{
+}
+
+
+// ::FLUSH()
+//
+// Called periodically, requesting that the gui code flush all pending
+// screen update requests.
+
+ void
+bx_nogui_gui_c::flush(void)
+{
+}
+
+
+// ::CLEAR_SCREEN()
+//
+// Called to request that the VGA region is cleared. Don't
+// clear the area that defines the headerbar.
+
+ void
+bx_nogui_gui_c::clear_screen(void)
+{
+}
+
+
+
+// ::TEXT_UPDATE()
+//
+// Called in a VGA text mode, to update the screen with
+// new content.
+//
+// old_text: array of character/attributes making up the contents
+// of the screen from the last call. See below
+// new_text: array of character/attributes making up the current
+// contents, which should now be displayed. See below
+//
+// format of old_text & new_text: each is 4000 bytes long.
+// This represents 80 characters wide by 25 high, with
+// each character being 2 bytes. The first by is the
+// character value, the second is the attribute byte.
+// I currently don't handle the attribute byte.
+//
+// cursor_x: new x location of cursor
+// cursor_y: new y location of cursor
+
+ void
+bx_nogui_gui_c::text_update(Bit8u *old_text, Bit8u *new_text,
+ unsigned long cursor_x, unsigned long cursor_y,
+ bx_vga_tminfo_t tm_info, unsigned nrows)
+{
+ UNUSED(old_text);
+ UNUSED(new_text);
+ UNUSED(cursor_x);
+ UNUSED(cursor_y);
+ UNUSED(tm_info);
+ UNUSED(nrows);
+}
+
+ int
+bx_nogui_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes)
+{
+ UNUSED(bytes);
+ UNUSED(nbytes);
+ return 0;
+}
+
+ int
+bx_nogui_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len)
+{
+ UNUSED(text_snapshot);
+ UNUSED(len);
+ return 0;
+}
+
+
+// ::PALETTE_CHANGE()
+//
+// Allocate a color in the native GUI, for this color, and put
+// it in the colormap location 'index'.
+// returns: 0=no screen update needed (color map change has direct effect)
+// 1=screen updated needed (redraw using current colormap)
+
+ bx_bool
+bx_nogui_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue)
+{
+ UNUSED(index);
+ UNUSED(red);
+ UNUSED(green);
+ UNUSED(blue);
+ return(0);
+}
+
+
+// ::GRAPHICS_TILE_UPDATE()
+//
+// Called to request that a tile of graphics be drawn to the
+// screen, since info in this region has changed.
+//
+// tile: array of 8bit values representing a block of pixels with
+// dimension equal to the 'tilewidth' & 'tileheight' parameters to
+// ::specific_init(). Each value specifies an index into the
+// array of colors you allocated for ::palette_change()
+// x0: x origin of tile
+// y0: y origin of tile
+//
+// note: origin of tile and of window based on (0,0) being in the upper
+// left of the window.
+
+ void
+bx_nogui_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
+{
+ UNUSED(tile);
+ UNUSED(x0);
+ UNUSED(y0);
+}
+
+
+
+// ::DIMENSION_UPDATE()
+//
+// Called when the VGA mode changes it's X,Y dimensions.
+// Resize the window to this size, but you need to add on
+// the height of the headerbar to the Y value.
+//
+// x: new VGA x size
+// y: new VGA y size (add headerbar_y parameter from ::specific_init().
+// fheight: new VGA character height in text mode
+// fwidth : new VGA character width in text mode
+// bpp : bits per pixel in graphics mode
+
+ void
+bx_nogui_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp)
+{
+ UNUSED(x);
+ UNUSED(y);
+ UNUSED(fheight);
+ UNUSED(fwidth);
+ UNUSED(bpp);
+}
+
+
+// ::CREATE_BITMAP()
+//
+// Create a monochrome bitmap of size 'xdim' by 'ydim', which will
+// be drawn in the headerbar. Return an integer ID to the bitmap,
+// with which the bitmap can be referenced later.
+//
+// bmap: packed 8 pixels-per-byte bitmap. The pixel order is:
+// bit0 is the left most pixel, bit7 is the right most pixel.
+// xdim: x dimension of bitmap
+// ydim: y dimension of bitmap
+
+ unsigned
+bx_nogui_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim)
+{
+ UNUSED(bmap);
+ UNUSED(xdim);
+ UNUSED(ydim);
+ return(0);
+}
+
+
+// ::HEADERBAR_BITMAP()
+//
+// Called to install a bitmap in the bochs headerbar (toolbar).
+//
+// bmap_id: will correspond to an ID returned from
+// ::create_bitmap(). 'alignment' is either BX_GRAVITY_LEFT
+// or BX_GRAVITY_RIGHT, meaning install the bitmap in the next
+// available leftmost or rightmost space.
+// alignment: is either BX_GRAVITY_LEFT or BX_GRAVITY_RIGHT,
+// meaning install the bitmap in the next
+// available leftmost or rightmost space.
+// f: a 'C' function pointer to callback when the mouse is clicked in
+// the boundaries of this bitmap.
+
+ unsigned
+bx_nogui_gui_c::headerbar_bitmap(unsigned bmap_id, unsigned alignment, void (*f)(void))
+{
+ UNUSED(bmap_id);
+ UNUSED(alignment);
+ UNUSED(f);
+ return(0);
+}
+
+
+// ::SHOW_HEADERBAR()
+//
+// Show (redraw) the current headerbar, which is composed of
+// currently installed bitmaps.
+
+ void
+bx_nogui_gui_c::show_headerbar(void)
+{
+}
+
+
+// ::REPLACE_BITMAP()
+//
+// Replace the bitmap installed in the headerbar ID slot 'hbar_id',
+// with the one specified by 'bmap_id'. 'bmap_id' will have
+// been generated by ::create_bitmap(). The old and new bitmap
+// must be of the same size. This allows the bitmap the user
+// sees to change, when some action occurs. For example when
+// the user presses on the floppy icon, it then displays
+// the ejected status.
+//
+// hbar_id: headerbar slot ID
+// bmap_id: bitmap ID
+
+ void
+bx_nogui_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id)
+{
+ UNUSED(hbar_id);
+ UNUSED(bmap_id);
+}
+
+
+// ::EXIT()
+//
+// Called before bochs terminates, to allow for a graceful
+// exit from the native GUI mechanism.
+
+ void
+bx_nogui_gui_c::exit(void)
+{
+ BX_INFO(("bx_nogui_gui_c::exit() not implemented yet."));
+}
+
+ void
+bx_nogui_gui_c::mouse_enabled_changed_specific (bx_bool val)
+{
+}
diff --git a/tools/ioemu/gui/rfb.cc b/tools/ioemu/gui/rfb.cc
new file mode 100644
index 0000000000..f67f80e895
--- /dev/null
+++ b/tools/ioemu/gui/rfb.cc
@@ -0,0 +1,1508 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: rfb.cc,v 1.26.2.1 2004/02/02 22:35:30 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2000 Psyon.Org!
+//
+// Donald Becker
+// http://www.psyon.org
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_WITH_RFB
+
+#include "icon_bochs.h"
+#include "font/vga.bitmap.h"
+
+class bx_rfb_gui_c : public bx_gui_c {
+public:
+ bx_rfb_gui_c (void) {}
+ DECLARE_GUI_VIRTUAL_METHODS()
+};
+
+// declare one instance of the gui object and call macro to insert the
+// plugin code
+static bx_rfb_gui_c *theGui = NULL;
+IMPLEMENT_GUI_PLUGIN_CODE(rfb)
+
+#define LOG_THIS theGui->
+
+#ifdef WIN32
+
+#include <winsock.h>
+#include <process.h>
+#include "rfb.h"
+
+#else
+
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/errno.h>
+#include <pthread.h>
+typedef unsigned long CARD32;
+typedef unsigned short CARD16;
+typedef short INT16;
+typedef unsigned char CARD8;
+typedef int SOCKET;
+
+#endif
+
+#include "rfbproto.h"
+
+static bool keep_alive;
+static bool client_connected;
+
+#define BX_RFB_PORT_MIN 5900
+#define BX_RFB_PORT_MAX 5949
+static unsigned short rfbPort;
+
+// Headerbar stuff
+unsigned rfbBitmapCount = 0;
+struct {
+ char *bmap;
+ unsigned xdim;
+ unsigned ydim;
+} rfbBitmaps[BX_MAX_PIXMAPS];
+
+unsigned rfbHeaderbarBitmapCount = 0;
+struct {
+ unsigned int index;
+ unsigned int xorigin;
+ unsigned int yorigin;
+ unsigned int alignment;
+ void (*f)(void);
+} rfbHeaderbarBitmaps[BX_MAX_HEADERBAR_ENTRIES];
+
+//Keyboard stuff
+#define KEYBOARD true
+#define MOUSE false
+#define MAX_KEY_EVENTS 512
+struct {
+ bool type;
+ int key;
+ int down;
+ int x;
+ int y;
+} rfbKeyboardEvent[MAX_KEY_EVENTS];
+static unsigned long rfbKeyboardEvents = 0;
+static bool bKeyboardInUse = false;
+
+// Misc Stuff
+struct {
+ unsigned int x;
+ unsigned int y;
+ unsigned int width;
+ unsigned int height;
+ bool updated;
+} rfbUpdateRegion;
+
+static char *rfbScreen;
+static char rfbPallet[256];
+
+static long rfbDimensionX, rfbDimensionY;
+static long rfbStretchedX, rfbStretchedY;
+static long rfbHeaderbarY;
+static long rfbTileX = 0;
+static long rfbTileY = 0;
+static unsigned long rfbCursorX = 0;
+static unsigned long rfbCursorY = 0;
+static unsigned long rfbOriginLeft = 0;
+static unsigned long rfbOriginRight = 0;
+
+static unsigned int text_rows=25, text_cols=80;
+static unsigned int font_height=16, font_width=8;
+
+//static unsigned long ServerThread = 0;
+//static unsigned long ServerThreadID = 0;
+
+static SOCKET sGlobal;
+
+void ServerThreadInit(void *indata);
+void HandleRfbClient(SOCKET sClient);
+int ReadExact(int sock, char *buf, int len);
+int WriteExact(int sock, char *buf, int len);
+void DrawBitmap(int x, int y, int width, int height, char *bmap, char color, bool update_client);
+void DrawChar(int x, int y, int width, int height, int fonty, char *bmap, char color);
+void UpdateScreen(unsigned char *newBits, int x, int y, int width, int height, bool update_client);
+void SendUpdate(int x, int y, int width, int height);
+void StartThread();
+void rfbKeyPressed(Bit32u key, int press_release);
+void rfbMouseMove(int x, int y, int bmask);
+void DrawColorPallet();
+
+static const rfbPixelFormat BGR233Format = {
+ 8, 8, 1, 1, 7, 7, 3, 0, 3, 6
+};
+
+// Set this for the endian of your machine. 0 = big, 1 = little
+static const int rfbEndianTest = 1;
+
+#define Swap16(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))
+#define Swap32(l) (((l) >> 24) | (((l) & 0x00ff0000) >> 8) | (((l) & 0x0000ff00) << 8) | ((l) << 24))
+#define Swap16IfLE(s) (*(const char *)&rfbEndianTest ? Swap16(s) : (s))
+#define Swap32IfLE(l) (*(const char *)&rfbEndianTest ? Swap32(l) : (l))
+#define PF_EQ(x,y) ((x.bitsPerPixel == y.bitsPerPixel) && (x.depth == y.depth) && (x.trueColour == y.trueColour) && ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && (!x.trueColour || ((x.redMax == y.redMax) && (x.greenMax == y.greenMax) && (x.blueMax == y.blueMax) && (x.redShift == y.redShift) && (x.greenShift == y.greenShift) && (x.blueShift == y.blueShift))))
+
+// This file defines stubs for the GUI interface, which is a
+// place to start if you want to port bochs to a platform, for
+// which there is no support for your native GUI, or if you want to compile
+// bochs without any native GUI support (no output window or
+// keyboard input will be possible).
+// Look in 'x.cc', 'beos.cc', and 'win32.cc' for specific
+// implementations of this interface. -Kevin
+
+
+// ::SPECIFIC_INIT()
+//
+// Called from gui.cc, once upon program startup, to allow for the
+// specific GUI code (X11, BeOS, ...) to be initialized.
+//
+// argc, argv: not used right now, but the intention is to pass native GUI
+// specific options from the command line. (X11 options, BeOS options,...)
+//
+// tilewidth, tileheight: for optimization, graphics_tile_update() passes
+// only updated regions of the screen to the gui code to be redrawn.
+// These define the dimensions of a region (tile).
+// headerbar_y: A headerbar (toolbar) is display on the top of the
+// VGA window, showing floppy status, and other information. It
+// always assumes the width of the current VGA mode width, but
+// it's height is defined by this parameter.
+
+void bx_rfb_gui_c::specific_init(int argc, char **argv, unsigned tilewidth, unsigned tileheight, unsigned headerbar_y)
+{
+ unsigned char fc, vc;
+
+ put("RFB");
+ UNUSED(bochs_icon_bits);
+
+ // the ask menu doesn't work on the client side
+ io->set_log_action(LOGLEV_PANIC, ACT_FATAL);
+
+ rfbHeaderbarY = headerbar_y;
+ rfbDimensionX = 640;
+ rfbDimensionY = 480 + rfbHeaderbarY;
+ rfbStretchedX = rfbDimensionX;
+ rfbStretchedY = rfbDimensionY;
+ rfbTileX = tilewidth;
+ rfbTileY = tileheight;
+
+ for(int i = 0; i < 256; i++) {
+ for(int j = 0; j < 16; j++) {
+ vc = bx_vgafont[i].data[j];
+ fc = 0;
+ for (int b = 0; b < 8; b++) {
+ fc |= (vc & 0x01) << (7 - b);
+ vc >>= 1;
+ }
+ vga_charmap[i*32+j] = fc;
+ }
+ }
+
+ rfbScreen = (char *)malloc(rfbDimensionX * rfbDimensionY);
+ memset(&rfbPallet, 0, sizeof(rfbPallet));
+ rfbPallet[63] = (char)0xFF;
+
+ rfbUpdateRegion.x = rfbDimensionX;
+ rfbUpdateRegion.y = rfbDimensionY;
+ rfbUpdateRegion.width = 0;
+ rfbUpdateRegion.height = 0;
+ rfbUpdateRegion.updated = false;
+
+ keep_alive = true;
+ client_connected = false;
+ StartThread();
+
+#ifdef WIN32
+ Sleep(1000);
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
+#endif
+ if (bx_options.Oprivate_colormap->get ()) {
+ BX_ERROR(( "private_colormap option ignored." ));
+ }
+ int counter = 30;
+ while ((!client_connected) && (counter--)) {
+#ifdef WIN32
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ }
+ if (counter < 0) BX_PANIC(("timeout! no client present"));
+}
+
+bool InitWinsock()
+{
+#ifdef WIN32
+ WSADATA wsaData;
+ if(WSAStartup(MAKEWORD(1,1), &wsaData) != 0) return false;
+#endif
+ return true;
+}
+
+bool StopWinsock()
+{
+#ifdef WIN32
+ WSACleanup();
+#endif
+ return true;
+}
+
+void ServerThreadInit(void *indata)
+{
+ SOCKET sServer;
+ SOCKET sClient;
+ struct sockaddr_in sai;
+ unsigned int sai_size;
+ int port_ok = 0;
+
+#ifdef WIN32
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
+#endif
+ if(!InitWinsock()) {
+ BX_PANIC(( "could not initialize winsock."));
+ goto end_of_thread;
+ }
+
+ sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if(sServer == -1) {
+ BX_PANIC(( "could not create socket." ));
+ goto end_of_thread;
+ }
+ for (rfbPort = BX_RFB_PORT_MIN; rfbPort <= BX_RFB_PORT_MAX; rfbPort++) {
+ sai.sin_addr.s_addr = INADDR_ANY;
+ sai.sin_family = AF_INET;
+ sai.sin_port = htons(rfbPort);
+ BX_INFO (("Trying port %d", rfbPort));
+ if(bind(sServer, (struct sockaddr *)&sai, sizeof(sai)) == -1) {
+ BX_INFO(( "Could not bind socket."));
+ continue;
+ }
+ if(listen(sServer, SOMAXCONN) == -1) {
+ BX_INFO(( "Could not listen on socket."));
+ continue;
+ }
+ // success
+ port_ok = 1;
+ break;
+ }
+ if (!port_ok) {
+ BX_PANIC (("RFB could not bind any port between %d and %d\n",
+ BX_RFB_PORT_MIN,
+ BX_RFB_PORT_MAX));
+ goto end_of_thread;
+ }
+ BX_INFO (("listening for connections on port %i", rfbPort));
+ fprintf (stderr, "RFB: listening for connections on port %i\n", rfbPort);
+ sai_size = sizeof(sai);
+ while(keep_alive) {
+ sClient = accept(sServer, (struct sockaddr *)&sai, (socklen_t*)&sai_size);
+ if(sClient != -1) {
+ HandleRfbClient(sClient);
+ sGlobal = -1;
+ close(sClient);
+ } else {
+ close(sClient);
+ }
+ }
+
+end_of_thread:
+ StopWinsock();
+}
+
+void HandleRfbClient(SOCKET sClient)
+{
+ char rfbName[] = "Bochs-RFB";
+ rfbProtocolVersionMsg pv;
+ int one = 1;
+ CARD32 auth;
+ rfbClientInitMsg cim;
+ rfbServerInitMsg sim;
+
+ client_connected = true;
+ setsockopt(sClient, IPPROTO_TCP, TCP_NODELAY, (const char *)&one, sizeof(one));
+ fprintf(stderr, "# RFB: accepted client connection.\n");
+ sprintf(pv, rfbProtocolVersionFormat, rfbProtocolMajorVersion, rfbProtocolMinorVersion);
+
+ if(WriteExact(sClient, pv, sz_rfbProtocolVersionMsg) < 0) {
+ fprintf(stderr, "# ERROR: RFB: could not send protocol version.\n");
+ return;
+ }
+ if(ReadExact(sClient, pv, sz_rfbProtocolVersionMsg) < 0) {
+ fprintf(stderr, "# ERROR: RFB: could not recieve client protocol version.\n");
+ return;
+ }
+
+ auth = Swap32IfLE(rfbNoAuth);
+ if(WriteExact(sClient, (char *)&auth, sizeof(auth)) < 0) {
+ fprintf(stderr, "# ERROR: RFB: could not send authorization method.\n");
+ return;
+ }
+
+ if(ReadExact(sClient, (char *)&cim, sz_rfbClientInitMsg) < 0) {
+ fprintf(stderr, "# ERROR: RFB: could not recieve client initialization message.\n");
+ return;
+ }
+
+ sim.framebufferWidth = Swap16IfLE((short)rfbDimensionX);
+ sim.framebufferHeight = Swap16IfLE((short)rfbDimensionY);
+ sim.format = BGR233Format;
+ sim.format.redMax = Swap16IfLE(sim.format.redMax);
+ sim.format.greenMax = Swap16IfLE(sim.format.greenMax);
+ sim.format.blueMax = Swap16IfLE(sim.format.blueMax);
+ sim.nameLength = strlen(rfbName);
+ sim.nameLength = Swap32IfLE(sim.nameLength);
+ if(WriteExact(sClient, (char *)&sim, sz_rfbServerInitMsg) < 0) {
+ fprintf(stderr, "# ERROR: RFB: could send server initialization message.\n");
+ return;
+ }
+ if(WriteExact(sClient, rfbName, strlen(rfbName)) < 0) {
+ fprintf(stderr, "# ERROR: RFB: could not send server name.\n");
+ return;
+ }
+
+ sGlobal = sClient;
+ while(keep_alive) {
+ CARD8 msgType;
+ int n;
+
+ if((n = recv(sClient, (char *)&msgType, 1, MSG_PEEK)) <= 0) {
+ if(n == 0) {
+ fprintf(stderr, "# RFB: client closed connection.\n");
+ } else {
+ fprintf(stderr, "# RFB: error recieving data.\n");
+ }
+ return;
+ }
+
+ switch(msgType) {
+ case rfbSetPixelFormat:
+ {
+ rfbSetPixelFormatMsg spf;
+ ReadExact(sClient, (char *)&spf, sizeof(rfbSetPixelFormatMsg));
+
+ spf.format.bitsPerPixel = spf.format.bitsPerPixel;
+ spf.format.depth = spf.format.depth;
+ spf.format.trueColour = (spf.format.trueColour ? 1 : 0);
+ spf.format.bigEndian = (spf.format.bigEndian ? 1 : 0);
+ spf.format.redMax = Swap16IfLE(spf.format.redMax);
+ spf.format.greenMax = Swap16IfLE(spf.format.greenMax);
+ spf.format.blueMax = Swap16IfLE(spf.format.blueMax);
+ spf.format.redShift = spf.format.redShift;
+ spf.format.greenShift = spf.format.greenShift;
+ spf.format.blueShift = spf.format.blueShift;
+
+ if (!PF_EQ(spf.format, BGR233Format)) {
+ fprintf(stderr,"# ERROR: RFB: client has wrong pixel format\n");
+ //return;
+ }
+ break;
+ }
+ case rfbFixColourMapEntries:
+ {
+ rfbFixColourMapEntriesMsg fcme;
+ ReadExact(sClient, (char *)&fcme, sizeof(rfbFixColourMapEntriesMsg));
+ break;
+ }
+ case rfbSetEncodings:
+ {
+ rfbSetEncodingsMsg se;
+ int i;
+ CARD32 enc;
+ ReadExact(sClient, (char *)&se, sizeof(rfbSetEncodingsMsg));
+ se.nEncodings = Swap16IfLE(se.nEncodings);
+ for(i = 0; i < se.nEncodings; i++) {
+ if((n = ReadExact(sClient, (char *)&enc, sizeof(CARD32))) <= 0) {
+ if(n == 0) {
+ fprintf(stderr, "# RFB: client closed connection.\n");
+ } else {
+ fprintf(stderr, "# RFB: error recieving data.\n");
+ }
+ return;
+ }
+ }
+ break;
+ }
+ case rfbFramebufferUpdateRequest:
+ {
+ rfbFramebufferUpdateRequestMsg fur;
+
+ ReadExact(sClient, (char *)&fur, sizeof(rfbFramebufferUpdateRequestMsg));
+ if(!fur.incremental) {
+ rfbUpdateRegion.x = 0;
+ rfbUpdateRegion.y = 0;
+ rfbUpdateRegion.width = rfbDimensionX;
+ rfbUpdateRegion.height = rfbDimensionY;
+ rfbUpdateRegion.updated = true;
+ } //else {
+ // if(fur.x < rfbUpdateRegion.x) rfbUpdateRegion.x = fur.x;
+ // if(fur.y < rfbUpdateRegion.x) rfbUpdateRegion.y = fur.y;
+ // if(((fur.x + fur.w) - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = ((fur.x + fur.w) - rfbUpdateRegion.x);
+ // if(((fur.y + fur.h) - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = ((fur.y + fur.h) - rfbUpdateRegion.y);
+ //}
+ //rfbUpdateRegion.updated = true;
+ break;
+ }
+ case rfbKeyEvent:
+ {
+ rfbKeyEventMsg ke;
+ ReadExact(sClient, (char *)&ke, sizeof(rfbKeyEventMsg));
+ ke.key = Swap32IfLE(ke.key);
+ while(bKeyboardInUse);
+ bKeyboardInUse = true;
+ if (rfbKeyboardEvents >= MAX_KEY_EVENTS) break;
+ rfbKeyboardEvent[rfbKeyboardEvents].type = KEYBOARD;
+ rfbKeyboardEvent[rfbKeyboardEvents].key = ke.key;
+ rfbKeyboardEvent[rfbKeyboardEvents].down = ke.down;
+ rfbKeyboardEvents++;
+ bKeyboardInUse = false;
+ break;
+ }
+ case rfbPointerEvent:
+ {
+ rfbPointerEventMsg pe;
+ ReadExact(sClient, (char *)&pe, sizeof(rfbPointerEventMsg));
+ while(bKeyboardInUse);
+ bKeyboardInUse = true;
+ if (rfbKeyboardEvents >= MAX_KEY_EVENTS) break;
+ rfbKeyboardEvent[rfbKeyboardEvents].type = MOUSE;
+ rfbKeyboardEvent[rfbKeyboardEvents].x = Swap16IfLE(pe.x);
+ rfbKeyboardEvent[rfbKeyboardEvents].y = Swap16IfLE(pe.y);
+ rfbKeyboardEvent[rfbKeyboardEvents].down = pe.buttonMask;
+ rfbKeyboardEvents++;
+ bKeyboardInUse = false;
+ break;
+ }
+ case rfbClientCutText:
+ {
+ rfbClientCutTextMsg cct;
+ ReadExact(sClient, (char *)&cct, sizeof(rfbClientCutTextMsg));
+ break;
+ }
+ }
+ }
+}
+// ::HANDLE_EVENTS()
+//
+// Called periodically (vga_update_interval in .bochsrc) so the
+// the gui code can poll for keyboard, mouse, and other
+// relevant events.
+
+void bx_rfb_gui_c::handle_events(void)
+{
+ unsigned int i = 0;
+ while(bKeyboardInUse);
+ bKeyboardInUse = true;
+ if(rfbKeyboardEvents > 0) {
+ for(i = 0; i < rfbKeyboardEvents; i++) {
+ if(rfbKeyboardEvent[i].type == KEYBOARD) {
+ rfbKeyPressed(rfbKeyboardEvent[i].key, rfbKeyboardEvent[i].down);
+ } else { //type == MOUSE;
+ rfbMouseMove(rfbKeyboardEvent[i].x, rfbKeyboardEvent[i].y, rfbKeyboardEvent[i].down);
+ }
+ }
+ rfbKeyboardEvents = 0;
+ }
+ bKeyboardInUse = false;
+
+ if(rfbUpdateRegion.updated) {
+ SendUpdate(rfbUpdateRegion.x, rfbUpdateRegion.y, rfbUpdateRegion.width, rfbUpdateRegion.height);
+ rfbUpdateRegion.x = rfbDimensionX;
+ rfbUpdateRegion.y = rfbDimensionY;
+ rfbUpdateRegion.width = 0;
+ rfbUpdateRegion.height = 0;
+ }
+ rfbUpdateRegion.updated = false;
+}
+
+
+// ::FLUSH()
+//
+// Called periodically, requesting that the gui code flush all pending
+// screen update requests.
+
+void bx_rfb_gui_c::flush(void)
+{
+}
+
+
+// ::CLEAR_SCREEN()
+//
+// Called to request that the VGA region is cleared. Don't
+// clear the area that defines the headerbar.
+void bx_rfb_gui_c::clear_screen(void)
+{
+ memset(&rfbScreen[rfbDimensionX * rfbHeaderbarY], 0, rfbDimensionX * (rfbDimensionY - rfbHeaderbarY));
+}
+
+
+
+// ::TEXT_UPDATE()
+//
+// Called in a VGA text mode, to update the screen with
+// new content.
+//
+// old_text: array of character/attributes making up the contents
+// of the screen from the last call. See below
+// new_text: array of character/attributes making up the current
+// contents, which should now be displayed. See below
+//
+// format of old_text & new_text: each is 4000 bytes long.
+// This represents 80 characters wide by 25 high, with
+// each character being 2 bytes. The first by is the
+// character value, the second is the attribute byte.
+// I currently don't handle the attribute byte.
+//
+// cursor_x: new x location of cursor
+// cursor_y: new y location of cursor
+
+void bx_rfb_gui_c::text_update(Bit8u *old_text, Bit8u *new_text, unsigned long cursor_x, unsigned long cursor_y, bx_vga_tminfo_t tm_info, unsigned nrows)
+{
+ unsigned char *old_line, *new_line;
+ unsigned char cAttr, cChar;
+ unsigned int curs, hchars, offset, rows, x, y, xc, yc;
+ bx_bool force_update=0;
+
+ UNUSED(nrows);
+
+ if(charmap_updated) {
+ force_update = 1;
+ charmap_updated = 0;
+ }
+
+ // first invalidate character at previous and new cursor location
+ if ( (rfbCursorY < text_rows) && (rfbCursorX < text_cols) ) {
+ curs = rfbCursorY * tm_info.line_offset + rfbCursorX * 2;
+ old_text[curs] = ~new_text[curs];
+ }
+ if((tm_info.cs_start <= tm_info.cs_end) && (tm_info.cs_start < font_height) &&
+ (cursor_y < text_rows) && (cursor_x < text_cols)) {
+ curs = cursor_y * tm_info.line_offset + cursor_x * 2;
+ old_text[curs] = ~new_text[curs];
+ } else {
+ curs = 0xffff;
+ }
+
+ rows = text_rows;
+ y = 0;
+ do {
+ hchars = text_cols;
+ new_line = new_text;
+ old_line = old_text;
+ offset = y * tm_info.line_offset;
+ yc = y * font_height + rfbHeaderbarY;
+ x = 0;
+ do {
+ if (force_update || (old_text[0] != new_text[0])
+ || (old_text[1] != new_text[1])) {
+ cChar = new_text[0];
+ cAttr = new_text[1];
+ xc = x * 8;
+ DrawChar(xc, yc, 8, font_height, 0, (char *)&vga_charmap[cChar<<5], cAttr);
+ if(yc < rfbUpdateRegion.y) rfbUpdateRegion.y = yc;
+ if((yc + font_height - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = (yc + font_height - rfbUpdateRegion.y);
+ if(xc < rfbUpdateRegion.x) rfbUpdateRegion.x = xc;
+ if((xc + 8 - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = (xc + 8 - rfbUpdateRegion.x);
+ rfbUpdateRegion.updated = true;
+ if (offset == curs) {
+ cAttr = ((cAttr >> 4) & 0xF) + ((cAttr & 0xF) << 4);
+ DrawChar(xc, yc + tm_info.cs_start, 8, tm_info.cs_end - tm_info.cs_start + 1,
+ tm_info.cs_start, (char *)&vga_charmap[cChar<<5], cAttr);
+ }
+ }
+ x++;
+ new_text+=2;
+ old_text+=2;
+ offset+=2;
+ } while (--hchars);
+ y++;
+ new_text = new_line + tm_info.line_offset;
+ old_text = old_line + tm_info.line_offset;
+ } while (--rows);
+
+ rfbCursorX = cursor_x;
+ rfbCursorY = cursor_y;
+}
+
+ int
+bx_rfb_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes)
+{
+ return 0;
+}
+
+ int
+bx_rfb_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len)
+{
+ return 0;
+}
+
+
+// ::PALETTE_CHANGE()
+//
+// Allocate a color in the native GUI, for this color, and put
+// it in the colormap location 'index'.
+// returns: 0=no screen update needed (color map change has direct effect)
+// 1=screen updated needed (redraw using current colormap)
+
+bx_bool bx_rfb_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue)
+{
+ rfbPallet[index] = (((red * 7 + 127) / 255) << 0) | (((green * 7 + 127) / 255) << 3) | (((blue * 3 + 127) / 255) << 6);
+ return(1);
+}
+
+
+// ::GRAPHICS_TILE_UPDATE()
+//
+// Called to request that a tile of graphics be drawn to the
+// screen, since info in this region has changed.
+//
+// tile: array of 8bit values representing a block of pixels with
+// dimension equal to the 'tilewidth' & 'tileheight' parameters to
+// ::specific_init(). Each value specifies an index into the
+// array of colors you allocated for ::palette_change()
+// x0: x origin of tile
+// y0: y origin of tile
+//
+// note: origin of tile and of window based on (0,0) being in the upper
+// left of the window.
+void bx_rfb_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
+{
+ UpdateScreen(tile, x0, y0 + rfbHeaderbarY, rfbTileX, rfbTileY, false);
+ if(x0 < rfbUpdateRegion.x) rfbUpdateRegion.x = x0;
+ if((y0 + rfbHeaderbarY) < rfbUpdateRegion.y) rfbUpdateRegion.y = y0 + rfbHeaderbarY;
+ if(((y0 + rfbHeaderbarY + rfbTileY) - rfbUpdateRegion.y) > rfbUpdateRegion.height) rfbUpdateRegion.height = ((y0 + rfbHeaderbarY + rfbTileY) - rfbUpdateRegion.y);
+ if(((x0 + rfbTileX) - rfbUpdateRegion.x) > rfbUpdateRegion.width) rfbUpdateRegion.width = ((x0 + rfbTileX) - rfbUpdateRegion.x);
+ rfbUpdateRegion.updated = true;
+}
+
+
+
+// ::DIMENSION_UPDATE()
+//
+// Called when the VGA mode changes it's X,Y dimensions.
+// Resize the window to this size, but you need to add on
+// the height of the headerbar to the Y value.
+//
+// x: new VGA x size
+// y: new VGA y size (add headerbar_y parameter from ::specific_init().
+// fheight: new VGA character height in text mode
+// fwidth : new VGA character width in text mode
+// bpp : bits per pixel in graphics mode
+
+ void
+bx_rfb_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp)
+{
+ if (bpp > 8) {
+ BX_PANIC(("%d bpp graphics mode not supported yet", bpp));
+ }
+ if (fheight > 0) {
+ font_height = fheight;
+ font_width = fwidth;
+ text_cols = x / fwidth;
+ text_rows = y / fheight;
+ } else {
+ if ((x > 640) || (y > 480)) {
+ BX_PANIC(("dimension_update(): RFB doesn't support graphics modes > 640x480 (%dx%d)", x, y));
+ }
+ }
+}
+
+
+// ::CREATE_BITMAP()
+//
+// Create a monochrome bitmap of size 'xdim' by 'ydim', which will
+// be drawn in the headerbar. Return an integer ID to the bitmap,
+// with which the bitmap can be referenced later.
+//
+// bmap: packed 8 pixels-per-byte bitmap. The pixel order is:
+// bit0 is the left most pixel, bit7 is the right most pixel.
+// xdim: x dimension of bitmap
+// ydim: y dimension of bitmap
+
+unsigned bx_rfb_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim)
+{
+ if(rfbBitmapCount >= BX_MAX_PIXMAPS) {
+ fprintf(stderr, "# RFB: too many pixmaps.\n");
+ return 0;
+ }
+ rfbBitmaps[rfbBitmapCount].bmap = (char *)malloc((xdim * ydim) / 8);
+ rfbBitmaps[rfbBitmapCount].xdim = xdim;
+ rfbBitmaps[rfbBitmapCount].ydim = ydim;
+ memcpy(rfbBitmaps[rfbBitmapCount].bmap, bmap, (xdim * ydim) / 8);
+
+ rfbBitmapCount++;
+ return(rfbBitmapCount - 1);
+}
+
+
+// ::HEADERBAR_BITMAP()
+//
+// Called to install a bitmap in the bochs headerbar (toolbar).
+//
+// bmap_id: will correspond to an ID returned from
+// ::create_bitmap(). 'alignment' is either BX_GRAVITY_LEFT
+// or BX_GRAVITY_RIGHT, meaning install the bitmap in the next
+// available leftmost or rightmost space.
+// alignment: is either BX_GRAVITY_LEFT or BX_GRAVITY_RIGHT,
+// meaning install the bitmap in the next
+// available leftmost or rightmost space.
+// f: a 'C' function pointer to callback when the mouse is clicked in
+// the boundaries of this bitmap.
+
+unsigned bx_rfb_gui_c::headerbar_bitmap(unsigned bmap_id, unsigned alignment, void (*f)(void))
+{
+ int hb_index;
+
+ if((rfbHeaderbarBitmapCount + 1) > BX_MAX_HEADERBAR_ENTRIES) {
+ return 0;
+ }
+
+ rfbHeaderbarBitmapCount++;
+ hb_index = rfbHeaderbarBitmapCount - 1;
+ rfbHeaderbarBitmaps[hb_index].index = bmap_id;
+ rfbHeaderbarBitmaps[hb_index].alignment = alignment;
+ rfbHeaderbarBitmaps[hb_index].f = f;
+ if (alignment == BX_GRAVITY_LEFT) {
+ rfbHeaderbarBitmaps[hb_index].xorigin = rfbOriginLeft;
+ rfbHeaderbarBitmaps[hb_index].yorigin = 0;
+ rfbOriginLeft += rfbBitmaps[bmap_id].xdim;
+ } else { // BX_GRAVITY_RIGHT
+ rfbOriginRight += rfbBitmaps[bmap_id].xdim;
+ rfbHeaderbarBitmaps[hb_index].xorigin = rfbOriginRight;
+ rfbHeaderbarBitmaps[hb_index].yorigin = 0;
+ }
+ return hb_index;
+}
+
+
+// ::SHOW_HEADERBAR()
+//
+// Show (redraw) the current headerbar, which is composed of
+// currently installed bitmaps.
+
+void bx_rfb_gui_c::show_headerbar(void)
+{
+ char *newBits;
+ unsigned int i, xorigin;
+
+ newBits = (char *)malloc(rfbDimensionX * rfbHeaderbarY);
+ memset(newBits, 0, (rfbDimensionX * rfbHeaderbarY));
+ DrawBitmap(0, 0, rfbDimensionX, rfbHeaderbarY, newBits, (char)0xf0, false);
+ for(i = 0; i < rfbHeaderbarBitmapCount; i++) {
+ if(rfbHeaderbarBitmaps[i].alignment == BX_GRAVITY_LEFT) {
+ xorigin = rfbHeaderbarBitmaps[i].xorigin;
+ } else {
+ xorigin = rfbDimensionX - rfbHeaderbarBitmaps[i].xorigin;
+ }
+ DrawBitmap(xorigin, 0, rfbBitmaps[rfbHeaderbarBitmaps[i].index].xdim, rfbBitmaps[rfbHeaderbarBitmaps[i].index].ydim, rfbBitmaps[rfbHeaderbarBitmaps[i].index].bmap, (char)0xf0, false);
+ }
+ free(newBits);
+}
+
+
+// ::REPLACE_BITMAP()
+//
+// Replace the bitmap installed in the headerbar ID slot 'hbar_id',
+// with the one specified by 'bmap_id'. 'bmap_id' will have
+// been generated by ::create_bitmap(). The old and new bitmap
+// must be of the same size. This allows the bitmap the user
+// sees to change, when some action occurs. For example when
+// the user presses on the floppy icon, it then displays
+// the ejected status.
+//
+// hbar_id: headerbar slot ID
+// bmap_id: bitmap ID
+
+void bx_rfb_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id)
+{
+ rfbHeaderbarBitmaps[hbar_id].index = bmap_id;
+}
+
+
+// ::EXIT()
+//
+// Called before bochs terminates, to allow for a graceful
+// exit from the native GUI mechanism.
+void bx_rfb_gui_c::exit(void)
+{
+ unsigned int i;
+ keep_alive = false;
+ StopWinsock();
+ free(rfbScreen);
+ for(i = 0; i < rfbBitmapCount; i++) {
+ free(rfbBitmaps[i].bmap);
+ }
+ fprintf(stderr, "# RFB: bx_rfb_gui_c::exit()\n");
+}
+
+/*
+* ReadExact reads an exact number of bytes on a TCP socket. Returns 1 if
+* those bytes have been read, 0 if the other end has closed, or -1 if an error
+* occurred (errno is set to ETIMEDOUT if it timed out).
+*/
+
+int ReadExact(int sock, char *buf, int len)
+{
+ int n;
+
+ while (len > 0) {
+ n = recv(sock, buf, len, 0);
+ if (n > 0) {
+ buf += n;
+ len -= n;
+ } else {
+ return n;
+ }
+ }
+ return 1;
+}
+
+/*
+* WriteExact writes an exact number of bytes on a TCP socket. Returns 1 if
+* those bytes have been written, or -1 if an error occurred (errno is set to
+* ETIMEDOUT if it timed out).
+*/
+
+int WriteExact(int sock, char *buf, int len)
+{
+ int n;
+
+ while (len > 0) {
+ n = send(sock, buf, len,0);
+
+ if (n > 0) {
+ buf += n;
+ len -= n;
+ } else if (n == 0) {
+ fprintf(stderr,"WriteExact: write returned 0?\n");
+ return n;
+ } else {
+ return n;
+ }
+ }
+ return 1;
+}
+
+void DrawBitmap(int x, int y, int width, int height, char *bmap, char color, bool update_client)
+{
+ int i;
+ unsigned char *newBits;
+ char fgcolor, bgcolor;
+ char vgaPallet[] = { (char)0x00, //Black
+ (char)0x01, //Dark Blue
+ (char)0x02, //Dark Green
+ (char)0x03, //Dark Cyan
+ (char)0x04, //Dark Red
+ (char)0x05, //Dark Magenta
+ (char)0x06, //Brown
+ (char)0x07, //Light Gray
+ (char)0x38, //Dark Gray
+ (char)0x09, //Light Blue
+ (char)0x12, //Green
+ (char)0x1B, //Cyan
+ (char)0x24, //Light Red
+ (char)0x2D, //Magenta
+ (char)0x36, //Yellow
+ (char)0x3F //White
+ };
+
+ bgcolor = vgaPallet[(color >> 4) & 0xF];
+ fgcolor = vgaPallet[color & 0xF];
+ newBits = (unsigned char *)malloc(width * height);
+ memset(newBits, 0, (width * height));
+ for(i = 0; i < (width * height) / 8; i++) {
+ newBits[i * 8 + 0] = (bmap[i] & 0x01) ? fgcolor : bgcolor;
+ newBits[i * 8 + 1] = (bmap[i] & 0x02) ? fgcolor : bgcolor;
+ newBits[i * 8 + 2] = (bmap[i] & 0x04) ? fgcolor : bgcolor;
+ newBits[i * 8 + 3] = (bmap[i] & 0x08) ? fgcolor : bgcolor;
+ newBits[i * 8 + 4] = (bmap[i] & 0x10) ? fgcolor : bgcolor;
+ newBits[i * 8 + 5] = (bmap[i] & 0x20) ? fgcolor : bgcolor;
+ newBits[i * 8 + 6] = (bmap[i] & 0x40) ? fgcolor : bgcolor;
+ newBits[i * 8 + 7] = (bmap[i] & 0x80) ? fgcolor : bgcolor;
+ }
+ UpdateScreen(newBits, x, y, width, height, update_client);
+ //DrawColorPallet();
+ free(newBits);
+}
+
+void DrawChar(int x, int y, int width, int height, int fonty, char *bmap, char color)
+{
+ static unsigned char newBits[8 * 32];
+ unsigned char mask;
+ int bytes = width * height;
+ char fgcolor, bgcolor;
+ char vgaPallet[] = { (char)0x00, //Black
+ (char)0x01, //Dark Blue
+ (char)0x02, //Dark Green
+ (char)0x03, //Dark Cyan
+ (char)0x04, //Dark Red
+ (char)0x05, //Dark Magenta
+ (char)0x06, //Brown
+ (char)0x07, //Light Gray
+ (char)0x38, //Dark Gray
+ (char)0x09, //Light Blue
+ (char)0x12, //Green
+ (char)0x1B, //Cyan
+ (char)0x24, //Light Red
+ (char)0x2D, //Magenta
+ (char)0x36, //Yellow
+ (char)0x3F //White
+ };
+
+ bgcolor = vgaPallet[(color >> 4) & 0xF];
+ fgcolor = vgaPallet[color & 0xF];
+
+ for(int i = 0; i < bytes; i+=width) {
+ mask = 0x80;
+ for(int j = 0; j < width; j++) {
+ newBits[i + j] = (bmap[fonty] & mask) ? fgcolor : bgcolor;
+ mask >>= 1;
+ }
+ fonty++;
+ }
+ UpdateScreen(newBits, x, y, width, height, false);
+ //DrawColorPallet();
+}
+
+void DrawColorPallet()
+{
+ unsigned char bits[100];
+ int x = 0, y = 0, c;
+ for(c = 0; c < 256; c++) {
+ memset(&bits, rfbPallet[c], 100);
+ UpdateScreen(bits, x, y, 10, 10, false);
+ x += 10;
+ if(x > 70) {
+ y += 10;
+ x = 0;
+ }
+ }
+}
+
+void UpdateScreen(unsigned char *newBits, int x, int y, int width, int height, bool update_client)
+{
+ int i, c;
+ for(i = 0; i < height; i++) {
+ for(c = 0; c < width; c++) {
+ newBits[(i * width) + c] = rfbPallet[newBits[(i * width) + c]];
+ }
+ memcpy(&rfbScreen[y * rfbDimensionX + x], &newBits[i * width], width);
+ y++;
+ }
+ if(update_client) {
+ if(sGlobal == -1) return;
+ rfbFramebufferUpdateMsg fum;
+ rfbFramebufferUpdateRectHeader furh;
+ fum.type = rfbFramebufferUpdate;
+ fum.nRects = Swap16IfLE(1);
+ WriteExact(sGlobal, (char *)&fum, sz_rfbFramebufferUpdateMsg);
+ furh.r.x = Swap16IfLE(x);
+ furh.r.y = Swap16IfLE((y - i));
+ furh.r.w = Swap16IfLE((short)width);
+ furh.r.h = Swap16IfLE((short)height);
+ furh.encoding = Swap32IfLE(rfbEncodingRaw);
+ WriteExact(sGlobal, (char *)&furh, sz_rfbFramebufferUpdateRectHeader);
+ WriteExact(sGlobal, (char *)newBits, width * height);
+ }
+}
+
+void SendUpdate(int x, int y, int width, int height)
+{
+ char *newBits;
+ int i;
+
+ if(x < 0 || y < 0 || (x + width) > rfbDimensionX || (y + height) > rfbDimensionY) {
+ fprintf(stderr, "# RFB: Dimensions out of bounds. x=%i y=%i w=%i h=%i\n", x, y, width, height);
+ }
+ if(sGlobal != -1) {
+ rfbFramebufferUpdateMsg fum;
+ rfbFramebufferUpdateRectHeader furh;
+
+ fum.type = rfbFramebufferUpdate;
+ fum.nRects = Swap16IfLE(1);
+
+ furh.r.x = Swap16IfLE(x);
+ furh.r.y = Swap16IfLE(y);
+ furh.r.w = Swap16IfLE((short)width);
+ furh.r.h = Swap16IfLE((short)height);
+ furh.encoding = Swap32IfLE(rfbEncodingRaw);
+
+ newBits = (char *)malloc(width * height);
+ for(i = 0; i < height; i++) {
+ memcpy(&newBits[i * width], &rfbScreen[y * rfbDimensionX + x], width);
+ y++;
+ }
+
+ WriteExact(sGlobal, (char *)&fum, sz_rfbFramebufferUpdateMsg);
+ WriteExact(sGlobal, (char *)&furh, sz_rfbFramebufferUpdateRectHeader);
+ WriteExact(sGlobal, (char *)newBits, width * height);
+
+ free(newBits);
+ }
+}
+
+void StartThread()
+{
+#ifdef WIN32
+ _beginthread(ServerThreadInit, 0, NULL);
+#else
+ pthread_t thread;
+ pthread_create(&thread, NULL, (void *(*)(void *))&ServerThreadInit, NULL);
+#endif
+}
+
+/***********************/
+/* Keyboard Definitons */
+/* And */
+/* Functions */
+/***********************/
+
+#define XK_space 0x020
+#define XK_asciitilde 0x07e
+
+#define XK_dead_grave 0xFE50
+#define XK_dead_acute 0xFE51
+#define XK_dead_circumflex 0xFE52
+#define XK_dead_tilde 0xFE53
+
+#define XK_BackSpace 0xFF08
+#define XK_Tab 0xFF09
+#define XK_Linefeed 0xFF0A
+#define XK_Clear 0xFF0B
+#define XK_Return 0xFF0D
+#define XK_Pause 0xFF13
+#define XK_Scroll_Lock 0xFF14
+#define XK_Sys_Req 0xFF15
+#define XK_Escape 0xFF1B
+
+#define XK_Delete 0xFFFF
+
+#define XK_Home 0xFF50
+#define XK_Left 0xFF51
+#define XK_Up 0xFF52
+#define XK_Right 0xFF53
+#define XK_Down 0xFF54
+#define XK_Page_Up 0xFF55
+#define XK_Page_Down 0xFF56
+#define XK_End 0xFF57
+#define XK_Begin 0xFF58
+
+#define XK_Select 0xFF60
+#define XK_Print 0xFF61
+#define XK_Execute 0xFF62
+#define XK_Insert 0xFF63
+
+#define XK_Cancel 0xFF69
+#define XK_Help 0xFF6A
+#define XK_Break 0xFF6B
+#define XK_Num_Lock 0xFF7F
+
+#define XK_KP_Space 0xFF80
+#define XK_KP_Tab 0xFF89
+#define XK_KP_Enter 0xFF8D
+
+#define XK_KP_Home 0xFF95
+#define XK_KP_Left 0xFF96
+#define XK_KP_Up 0xFF97
+#define XK_KP_Right 0xFF98
+#define XK_KP_Down 0xFF99
+#define XK_KP_Prior 0xFF9A
+#define XK_KP_Page_Up 0xFF9A
+#define XK_KP_Next 0xFF9B
+#define XK_KP_Page_Down 0xFF9B
+#define XK_KP_End 0xFF9C
+#define XK_KP_Begin 0xFF9D
+#define XK_KP_Insert 0xFF9E
+#define XK_KP_Delete 0xFF9F
+#define XK_KP_Equal 0xFFBD
+#define XK_KP_Multiply 0xFFAA
+#define XK_KP_Add 0xFFAB
+#define XK_KP_Separator 0xFFAC
+#define XK_KP_Subtract 0xFFAD
+#define XK_KP_Decimal 0xFFAE
+#define XK_KP_Divide 0xFFAF
+
+#define XK_KP_F1 0xFF91
+#define XK_KP_F2 0xFF92
+#define XK_KP_F3 0xFF93
+#define XK_KP_F4 0xFF94
+
+#define XK_KP_0 0xFFB0
+#define XK_KP_1 0xFFB1
+#define XK_KP_2 0xFFB2
+#define XK_KP_3 0xFFB3
+#define XK_KP_4 0xFFB4
+#define XK_KP_5 0xFFB5
+#define XK_KP_6 0xFFB6
+#define XK_KP_7 0xFFB7
+#define XK_KP_8 0xFFB8
+#define XK_KP_9 0xFFB9
+
+#define XK_F1 0xFFBE
+#define XK_F2 0xFFBF
+#define XK_F3 0xFFC0
+#define XK_F4 0xFFC1
+#define XK_F5 0xFFC2
+#define XK_F6 0xFFC3
+#define XK_F7 0xFFC4
+#define XK_F8 0xFFC5
+#define XK_F9 0xFFC6
+#define XK_F10 0xFFC7
+#define XK_F11 0xFFC8
+#define XK_F12 0xFFC9
+#define XK_F13 0xFFCA
+#define XK_F14 0xFFCB
+#define XK_F15 0xFFCC
+#define XK_F16 0xFFCD
+#define XK_F17 0xFFCE
+#define XK_F18 0xFFCF
+#define XK_F19 0xFFD0
+#define XK_F20 0xFFD1
+#define XK_F21 0xFFD2
+#define XK_F22 0xFFD3
+#define XK_F23 0xFFD4
+#define XK_F24 0xFFD5
+
+
+#define XK_Shift_L 0xFFE1
+#define XK_Shift_R 0xFFE2
+#define XK_Control_L 0xFFE3
+#define XK_Control_R 0xFFE4
+#define XK_Caps_Lock 0xFFE5
+#define XK_Shift_Lock 0xFFE6
+#define XK_Meta_L 0xFFE7
+#define XK_Meta_R 0xFFE8
+#define XK_Alt_L 0xFFE9
+#define XK_Alt_R 0xFFEA
+
+Bit32u rfb_ascii_to_key_event[0x5f] = {
+ // !"#$%&'
+ BX_KEY_SPACE,
+ BX_KEY_1,
+ BX_KEY_SINGLE_QUOTE,
+ BX_KEY_3,
+ BX_KEY_4,
+ BX_KEY_5,
+ BX_KEY_7,
+ BX_KEY_SINGLE_QUOTE,
+
+ // ()*+,-./
+ BX_KEY_9,
+ BX_KEY_0,
+ BX_KEY_8,
+ BX_KEY_EQUALS,
+ BX_KEY_COMMA,
+ BX_KEY_MINUS,
+ BX_KEY_PERIOD,
+ BX_KEY_SLASH,
+
+ // 01234567
+ BX_KEY_0,
+ BX_KEY_1,
+ BX_KEY_2,
+ BX_KEY_3,
+ BX_KEY_4,
+ BX_KEY_5,
+ BX_KEY_6,
+ BX_KEY_7,
+
+ // 89:;<=>?
+ BX_KEY_8,
+ BX_KEY_9,
+ BX_KEY_SEMICOLON,
+ BX_KEY_SEMICOLON,
+ BX_KEY_COMMA,
+ BX_KEY_EQUALS,
+ BX_KEY_PERIOD,
+ BX_KEY_SLASH,
+
+ // @ABCDEFG
+ BX_KEY_2,
+ BX_KEY_A,
+ BX_KEY_B,
+ BX_KEY_C,
+ BX_KEY_D,
+ BX_KEY_E,
+ BX_KEY_F,
+ BX_KEY_G,
+
+
+ // HIJKLMNO
+ BX_KEY_H,
+ BX_KEY_I,
+ BX_KEY_J,
+ BX_KEY_K,
+ BX_KEY_L,
+ BX_KEY_M,
+ BX_KEY_N,
+ BX_KEY_O,
+
+
+ // PQRSTUVW
+ BX_KEY_P,
+ BX_KEY_Q,
+ BX_KEY_R,
+ BX_KEY_S,
+ BX_KEY_T,
+ BX_KEY_U,
+ BX_KEY_V,
+ BX_KEY_W,
+
+ // XYZ[\]^_
+ BX_KEY_X,
+ BX_KEY_Y,
+ BX_KEY_Z,
+ BX_KEY_LEFT_BRACKET,
+ BX_KEY_BACKSLASH,
+ BX_KEY_RIGHT_BRACKET,
+ BX_KEY_6,
+ BX_KEY_MINUS,
+
+ // `abcdefg
+ BX_KEY_GRAVE,
+ BX_KEY_A,
+ BX_KEY_B,
+ BX_KEY_C,
+ BX_KEY_D,
+ BX_KEY_E,
+ BX_KEY_F,
+ BX_KEY_G,
+
+ // hijklmno
+ BX_KEY_H,
+ BX_KEY_I,
+ BX_KEY_J,
+ BX_KEY_K,
+ BX_KEY_L,
+ BX_KEY_M,
+ BX_KEY_N,
+ BX_KEY_O,
+
+ // pqrstuvw
+ BX_KEY_P,
+ BX_KEY_Q,
+ BX_KEY_R,
+ BX_KEY_S,
+ BX_KEY_T,
+ BX_KEY_U,
+ BX_KEY_V,
+ BX_KEY_W,
+
+ // xyz{|}~
+ BX_KEY_X,
+ BX_KEY_Y,
+ BX_KEY_Z,
+ BX_KEY_LEFT_BRACKET,
+ BX_KEY_BACKSLASH,
+ BX_KEY_RIGHT_BRACKET,
+ BX_KEY_GRAVE
+ };
+
+void rfbKeyPressed(Bit32u key, int press_release)
+{
+ Bit32u key_event;
+
+ if((key >= XK_space) && (key <= XK_asciitilde)) {
+ key_event = rfb_ascii_to_key_event[key - XK_space];
+ } else {
+ switch (key) {
+ case XK_KP_1:
+#ifdef XK_KP_End
+ case XK_KP_End:
+#endif
+ key_event = BX_KEY_KP_END; break;
+
+ case XK_KP_2:
+#ifdef XK_KP_Down
+ case XK_KP_Down:
+#endif
+ key_event = BX_KEY_KP_DOWN; break;
+
+ case XK_KP_3:
+#ifdef XK_KP_Page_Down
+ case XK_KP_Page_Down:
+#endif
+ key_event = BX_KEY_KP_PAGE_DOWN; break;
+
+ case XK_KP_4:
+#ifdef XK_KP_Left
+ case XK_KP_Left:
+#endif
+ key_event = BX_KEY_KP_LEFT; break;
+
+ case XK_KP_5:
+#ifdef XK_KP_Begin
+ case XK_KP_Begin:
+#endif
+ key_event = BX_KEY_KP_5; break;
+
+ case XK_KP_6:
+#ifdef XK_KP_Right
+ case XK_KP_Right:
+#endif
+ key_event = BX_KEY_KP_RIGHT; break;
+
+ case XK_KP_7:
+#ifdef XK_KP_Home
+ case XK_KP_Home:
+#endif
+ key_event = BX_KEY_KP_HOME; break;
+
+ case XK_KP_8:
+#ifdef XK_KP_Up
+ case XK_KP_Up:
+#endif
+ key_event = BX_KEY_KP_UP; break;
+
+ case XK_KP_9:
+#ifdef XK_KP_Page_Up
+ case XK_KP_Page_Up:
+#endif
+ key_event = BX_KEY_KP_PAGE_UP; break;
+
+ case XK_KP_0:
+#ifdef XK_KP_Insert
+ case XK_KP_Insert:
+#endif
+ key_event = BX_KEY_KP_INSERT; break;
+
+ case XK_KP_Decimal:
+#ifdef XK_KP_Delete
+ case XK_KP_Delete:
+#endif
+ key_event = BX_KEY_KP_DELETE; break;
+
+#ifdef XK_KP_Enter
+ case XK_KP_Enter: key_event = BX_KEY_KP_ENTER; break;
+#endif
+
+ case XK_KP_Subtract: key_event = BX_KEY_KP_SUBTRACT; break;
+ case XK_KP_Add: key_event = BX_KEY_KP_ADD; break;
+
+ case XK_KP_Multiply: key_event = BX_KEY_KP_MULTIPLY; break;
+ case XK_KP_Divide: key_event = BX_KEY_KP_DIVIDE; break;
+
+
+ case XK_Up: key_event = BX_KEY_UP; break;
+ case XK_Down: key_event = BX_KEY_DOWN; break;
+ case XK_Left: key_event = BX_KEY_LEFT; break;
+ case XK_Right: key_event = BX_KEY_RIGHT; break;
+
+
+ case XK_Delete: key_event = BX_KEY_DELETE; break;
+ case XK_BackSpace: key_event = BX_KEY_BACKSPACE; break;
+ case XK_Tab: key_event = BX_KEY_TAB; break;
+#ifdef XK_ISO_Left_Tab
+ case XK_ISO_Left_Tab: key_event = BX_KEY_TAB; break;
+#endif
+ case XK_Return: key_event = BX_KEY_ENTER; break;
+ case XK_Escape: key_event = BX_KEY_ESC; break;
+ case XK_F1: key_event = BX_KEY_F1; break;
+ case XK_F2: key_event = BX_KEY_F2; break;
+ case XK_F3: key_event = BX_KEY_F3; break;
+ case XK_F4: key_event = BX_KEY_F4; break;
+ case XK_F5: key_event = BX_KEY_F5; break;
+ case XK_F6: key_event = BX_KEY_F6; break;
+ case XK_F7: key_event = BX_KEY_F7; break;
+ case XK_F8: key_event = BX_KEY_F8; break;
+ case XK_F9: key_event = BX_KEY_F9; break;
+ case XK_F10: key_event = BX_KEY_F10; break;
+ case XK_F11: key_event = BX_KEY_F11; break;
+ case XK_F12: key_event = BX_KEY_F12; break;
+ case XK_Control_L: key_event = BX_KEY_CTRL_L; break;
+#ifdef XK_Control_R
+ case XK_Control_R: key_event = BX_KEY_CTRL_R; break;
+#endif
+ case XK_Shift_L: key_event = BX_KEY_SHIFT_L; break;
+ case XK_Shift_R: key_event = BX_KEY_SHIFT_R; break;
+ case XK_Alt_L: key_event = BX_KEY_ALT_L; break;
+#ifdef XK_Alt_R
+ case XK_Alt_R: key_event = BX_KEY_ALT_R; break;
+#endif
+ case XK_Caps_Lock: key_event = BX_KEY_CAPS_LOCK; break;
+ case XK_Num_Lock: key_event = BX_KEY_NUM_LOCK; break;
+#ifdef XK_Scroll_Lock
+ case XK_Scroll_Lock: key_event = BX_KEY_SCRL_LOCK; break;
+#endif
+#ifdef XK_Print
+ case XK_Print: key_event = BX_KEY_PRINT; break;
+#endif
+#ifdef XK_Pause
+ case XK_Pause: key_event = BX_KEY_PAUSE; break;
+#endif
+
+ case XK_Insert: key_event = BX_KEY_INSERT; break;
+ case XK_Home: key_event = BX_KEY_HOME; break;
+ case XK_End: key_event = BX_KEY_END; break;
+ case XK_Page_Up: key_event = BX_KEY_PAGE_UP; break;
+ case XK_Page_Down: key_event = BX_KEY_PAGE_DOWN; break;
+
+ default:
+ BX_ERROR(("rfbKeyPress(): key %04x unhandled!", key));
+ fprintf(stderr, "RFB: rfbKeyPress(): key %04x unhandled!\n", key);
+ return;
+ break;
+ }
+ }
+
+ if (press_release) key_event |= BX_KEY_RELEASED;
+ DEV_kbd_gen_scancode(key_event);
+}
+
+void rfbMouseMove(int x, int y, int bmask)
+{
+ static int oldx = -1;
+ static int oldy = -1;
+ int xorigin;
+
+ if (oldx == oldy == -1) {
+ oldx = x;
+ oldy = y;
+ return;
+ }
+ if(y > rfbHeaderbarY) {
+ //DEV_mouse_motion(x, y - rfbHeaderbarY, buttons);
+ DEV_mouse_motion(x - oldx, oldy - y, bmask);
+ oldx = x;
+ oldy = y;
+ } else {
+ if (bmask == 1) {
+ for (unsigned i=0; i<rfbHeaderbarBitmapCount; i++) {
+ if (rfbHeaderbarBitmaps[i].alignment == BX_GRAVITY_LEFT)
+ xorigin = rfbHeaderbarBitmaps[i].xorigin;
+ else
+ xorigin = rfbDimensionX - rfbHeaderbarBitmaps[i].xorigin;
+ if ( (x>=xorigin) && (x<(xorigin+int(rfbBitmaps[rfbHeaderbarBitmaps[i].index].xdim))) ) {
+ rfbHeaderbarBitmaps[i].f();
+ return;
+ }
+ }
+ }
+ }
+}
+
+ void
+bx_rfb_gui_c::mouse_enabled_changed_specific (bx_bool val)
+{
+}
+
+#endif /* if BX_WITH_RFB */
diff --git a/tools/ioemu/gui/rfb.h b/tools/ioemu/gui/rfb.h
new file mode 100644
index 0000000000..948ac8252f
--- /dev/null
+++ b/tools/ioemu/gui/rfb.h
@@ -0,0 +1,35 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: rfb.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// rfb.h
+// This includes the rfb spec header, the port numbers,
+// the CARD type definitions and various useful macros.
+//
+
+#ifndef RFB_H__
+#define RFB_H__
+
+// Define the CARD* types as used in X11/Xmd.h
+
+typedef unsigned long CARD32;
+typedef unsigned short CARD16;
+typedef short INT16;
+typedef unsigned char CARD8;
+
+// Define the port number offsets
+#define FLASH_PORT_OFFSET 5400
+#define INCOMING_PORT_OFFSET 5500
+#define HTTP_PORT_OFFSET 5800 // we don't use this in Venice
+#define RFB_PORT_OFFSET 5900
+
+#define _SIZEOF(x) sz_##x
+#define SIZEOF(x) _SIZEOF(x)
+
+#define PORT_TO_DISPLAY(p) ( (p) - RFB_PORT_OFFSET )
+#define DISPLAY_TO_PORT(d) ( (d) + RFB_PORT_OFFSET )
+
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define ETIMEDOUT WSAETIMEDOUT
+
+#endif
diff --git a/tools/ioemu/gui/rfbproto.h b/tools/ioemu/gui/rfbproto.h
new file mode 100644
index 0000000000..a2be5f83c9
--- /dev/null
+++ b/tools/ioemu/gui/rfbproto.h
@@ -0,0 +1,675 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: rfbproto.h,v 1.2 2001/10/03 13:10:37 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+/*
+ * Copyright (C) 1997, 1998 Olivetti & Oracle Research Laboratory
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * rfbproto.h - header file for the RFB protocol version 3.3
+ *
+ * Uses types CARD<n> for an n-bit unsigned integer, INT<n> for an n-bit signed
+ * integer (for n = 8, 16 and 32).
+ *
+ * All multiple byte integers are in big endian (network) order (most
+ * significant byte first). Unless noted otherwise there is no special
+ * alignment of protocol structures.
+ *
+ *
+ * Once the initial handshaking is done, all messages start with a type byte,
+ * (usually) followed by message-specific data. The order of definitions in
+ * this file is as follows:
+ *
+ * (1) Structures used in several types of message.
+ * (2) Structures used in the initial handshaking.
+ * (3) Message types.
+ * (4) Encoding types.
+ * (5) For each message type, the form of the data following the type byte.
+ * Sometimes this is defined by a single structure but the more complex
+ * messages have to be explained by comments.
+ */
+
+
+/*****************************************************************************
+ *
+ * Structures used in several messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify a rectangle. This structure is a multiple of 4
+ * bytes so that it can be interspersed with 32-bit pixel data without
+ * affecting alignment.
+ */
+
+typedef struct {
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbRectangle;
+
+#define sz_rfbRectangle 8
+
+
+/*-----------------------------------------------------------------------------
+ * Structure used to specify pixel format.
+ */
+
+typedef struct {
+
+ CARD8 bitsPerPixel; /* 8,16,32 only */
+
+ CARD8 depth; /* 8 to 32 */
+
+ CARD8 bigEndian; /* True if multi-byte pixels are interpreted
+ as big endian, or if single-bit-per-pixel
+ has most significant bit of the byte
+ corresponding to first (leftmost) pixel. Of
+ course this is meaningless for 8 bits/pix */
+
+ CARD8 trueColour; /* If false then we need a "colour map" to
+ convert pixels to RGB. If true, xxxMax and
+ xxxShift specify bits used for red, green
+ and blue */
+
+ /* the following fields are only meaningful if trueColour is true */
+
+ CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the
+ number of bits used for red). Note this
+ value is always in big endian order. */
+
+ CARD16 greenMax; /* similar for green */
+
+ CARD16 blueMax; /* and blue */
+
+ CARD8 redShift; /* number of shifts needed to get the red
+ value in a pixel to the least significant
+ bit. To find the red value from a given
+ pixel, do the following:
+ 1) Swap pixel value according to bigEndian
+ (e.g. if bigEndian is false and host byte
+ order is big endian, then swap).
+ 2) Shift right by redShift.
+ 3) AND with redMax (in host byte order).
+ 4) You now have the red value between 0 and
+ redMax. */
+
+ CARD8 greenShift; /* similar for green */
+
+ CARD8 blueShift; /* and blue */
+
+ CARD8 pad1;
+ CARD16 pad2;
+
+} rfbPixelFormat;
+
+#define sz_rfbPixelFormat 16
+
+
+
+/*****************************************************************************
+ *
+ * Initial handshaking messages
+ *
+ *****************************************************************************/
+
+/*-----------------------------------------------------------------------------
+ * Protocol Version
+ *
+ * The server always sends 12 bytes to start which identifies the latest RFB
+ * protocol version number which it supports. These bytes are interpreted
+ * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where
+ * xxx and yyy are the major and minor version numbers (for version 3.3
+ * this is "RFB 003.003\n").
+ *
+ * The client then replies with a similar 12-byte message giving the version
+ * number of the protocol which should actually be used (which may be different
+ * to that quoted by the server).
+ *
+ * It is intended that both clients and servers may provide some level of
+ * backwards compatibility by this mechanism. Servers in particular should
+ * attempt to provide backwards compatibility, and even forwards compatibility
+ * to some extent. For example if a client demands version 3.1 of the
+ * protocol, a 3.0 server can probably assume that by ignoring requests for
+ * encoding types it doesn't understand, everything will still work OK. This
+ * will probably not be the case for changes in the major version number.
+ *
+ * The format string below can be used in sprintf or sscanf to generate or
+ * decode the version string respectively.
+ */
+
+#define rfbProtocolVersionFormat "RFB %03d.%03d\n"
+#define rfbProtocolMajorVersion 3
+#define rfbProtocolMinorVersion 3
+
+typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */
+
+#define sz_rfbProtocolVersionMsg 12
+
+
+/*-----------------------------------------------------------------------------
+ * Authentication
+ *
+ * Once the protocol version has been decided, the server then sends a 32-bit
+ * word indicating whether any authentication is needed on the connection.
+ * The value of this word determines the authentication scheme in use. For
+ * version 3.0 of the protocol this may have one of the following values:
+ */
+
+#define rfbConnFailed 0
+#define rfbNoAuth 1
+#define rfbVncAuth 2
+
+/*
+ * rfbConnFailed: For some reason the connection failed (e.g. the server
+ * cannot support the desired protocol version). This is
+ * followed by a string describing the reason (where a
+ * string is specified as a 32-bit length followed by that
+ * many ASCII characters).
+ *
+ * rfbNoAuth: No authentication is needed.
+ *
+ * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte
+ * challenge follows, which the client encrypts as
+ * appropriate using the password and sends the resulting
+ * 16-byte response. If the response is correct, the
+ * server sends the 32-bit word rfbVncAuthOK. If a simple
+ * failure happens, the server sends rfbVncAuthFailed and
+ * closes the connection. If the server decides that too
+ * many failures have occurred, it sends rfbVncAuthTooMany
+ * and closes the connection. In the latter case, the
+ * server should not allow an immediate reconnection by
+ * the client.
+ */
+
+#define rfbVncAuthOK 0
+#define rfbVncAuthFailed 1
+#define rfbVncAuthTooMany 2
+
+
+/*-----------------------------------------------------------------------------
+ * Client Initialisation Message
+ *
+ * Once the client and server are sure that they're happy to talk to one
+ * another, the client sends an initialisation message. At present this
+ * message only consists of a boolean indicating whether the server should try
+ * to share the desktop by leaving other clients connected, or give exclusive
+ * access to this client by disconnecting all other clients.
+ */
+
+typedef struct {
+ CARD8 shared;
+} rfbClientInitMsg;
+
+#define sz_rfbClientInitMsg 1
+
+
+/*-----------------------------------------------------------------------------
+ * Server Initialisation Message
+ *
+ * After the client initialisation message, the server sends one of its own.
+ * This tells the client the width and height of the server's framebuffer,
+ * its pixel format and the name associated with the desktop.
+ */
+
+typedef struct {
+ CARD16 framebufferWidth;
+ CARD16 framebufferHeight;
+ rfbPixelFormat format; /* the server's preferred pixel format */
+ CARD32 nameLength;
+ /* followed by char name[nameLength] */
+} rfbServerInitMsg;
+
+#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat)
+
+
+/*
+ * Following the server initialisation message it's up to the client to send
+ * whichever protocol messages it wants. Typically it will send a
+ * SetPixelFormat message and a SetEncodings message, followed by a
+ * FramebufferUpdateRequest. From then on the server will send
+ * FramebufferUpdate messages in response to the client's
+ * FramebufferUpdateRequest messages. The client should send
+ * FramebufferUpdateRequest messages with incremental set to true when it has
+ * finished processing one FramebufferUpdate and is ready to process another.
+ * With a fast client, the rate at which FramebufferUpdateRequests are sent
+ * should be regulated to avoid hogging the network.
+ */
+
+
+
+/*****************************************************************************
+ *
+ * Message types
+ *
+ *****************************************************************************/
+
+/* server -> client */
+
+#define rfbFramebufferUpdate 0
+#define rfbSetColourMapEntries 1
+#define rfbBell 2
+#define rfbServerCutText 3
+
+
+/* client -> server */
+
+#define rfbSetPixelFormat 0
+#define rfbFixColourMapEntries 1 /* not currently supported */
+#define rfbSetEncodings 2
+#define rfbFramebufferUpdateRequest 3
+#define rfbKeyEvent 4
+#define rfbPointerEvent 5
+#define rfbClientCutText 6
+
+
+
+
+/*****************************************************************************
+ *
+ * Encoding types
+ *
+ *****************************************************************************/
+
+#define rfbEncodingRaw 0
+#define rfbEncodingCopyRect 1
+#define rfbEncodingRRE 2
+#define rfbEncodingCoRRE 4
+#define rfbEncodingHextile 5
+
+
+
+/*****************************************************************************
+ *
+ * Server -> client message definitions
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdate - a block of rectangles to be copied to the framebuffer.
+ *
+ * This message consists of a header giving the number of rectangles of pixel
+ * data followed by the rectangles themselves. The header is padded so that
+ * together with the type byte it is an exact multiple of 4 bytes (to help
+ * with alignment of 32-bit pixels):
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdate */
+ CARD8 pad;
+ CARD16 nRects;
+ /* followed by nRects rectangles */
+} rfbFramebufferUpdateMsg;
+
+#define sz_rfbFramebufferUpdateMsg 4
+
+/*
+ * Each rectangle of pixel data consists of a header describing the position
+ * and size of the rectangle and a type word describing the encoding of the
+ * pixel data, followed finally by the pixel data. Note that if the client has
+ * not sent a SetEncodings message then it will only receive raw pixel data.
+ * Also note again that this structure is a multiple of 4 bytes.
+ */
+
+typedef struct {
+ rfbRectangle r;
+ CARD32 encoding; /* one of the encoding types rfbEncoding... */
+} rfbFramebufferUpdateRectHeader;
+
+#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4)
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Raw Encoding. Pixels are sent in top-to-bottom scanline order,
+ * left-to-right within a scanline with no padding in between.
+ */
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CopyRect Encoding. The pixels are specified simply by the x and y position
+ * of the source rectangle.
+ */
+
+typedef struct {
+ CARD16 srcX;
+ CARD16 srcY;
+} rfbCopyRect;
+
+#define sz_rfbCopyRect 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure
+ * giving the number of subrectangles following. Finally the data follows in
+ * the form [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbRectangle>].
+ */
+
+typedef struct {
+ CARD32 nSubrects;
+} rfbRREHeader;
+
+#define sz_rfbRREHeader 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving
+ * the number of subrectangles following. Finally the data follows in the form
+ * [<bgpixel><subrect><subrect>...] where each <subrect> is
+ * [<pixel><rfbCoRRERectangle>]. This means that
+ * the whole rectangle must be at most 255x255 pixels.
+ */
+
+typedef struct {
+ CARD8 x;
+ CARD8 y;
+ CARD8 w;
+ CARD8 h;
+} rfbCoRRERectangle;
+
+#define sz_rfbCoRRERectangle 4
+
+
+/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels,
+ * starting at the top left going in left-to-right, top-to-bottom order. If
+ * the width of the rectangle is not an exact multiple of 16 then the width of
+ * the last tile in each row will be correspondingly smaller. Similarly if the
+ * height is not an exact multiple of 16 then the height of each tile in the
+ * final row will also be smaller. Each tile begins with a "subencoding" type
+ * byte, which is a mask made up of a number of bits. If the Raw bit is set
+ * then the other bits are irrelevant; w*h pixel values follow (where w and h
+ * are the width and height of the tile). Otherwise the tile is encoded in a
+ * similar way to RRE, except that the position and size of each subrectangle
+ * can be specified in just two bytes. The other bits in the mask are as
+ * follows:
+ *
+ * BackgroundSpecified - if set, a pixel value follows which specifies
+ * the background colour for this tile. The first non-raw tile in a
+ * rectangle must have this bit set. If this bit isn't set then the
+ * background is the same as the last tile.
+ *
+ * ForegroundSpecified - if set, a pixel value follows which specifies
+ * the foreground colour to be used for all subrectangles in this tile.
+ * If this bit is set then the SubrectsColoured bit must be zero.
+ *
+ * AnySubrects - if set, a single byte follows giving the number of
+ * subrectangles following. If not set, there are no subrectangles (i.e.
+ * the whole tile is just solid background colour).
+ *
+ * SubrectsColoured - if set then each subrectangle is preceded by a pixel
+ * value giving the colour of that subrectangle. If not set, all
+ * subrectangles are the same colour, the foreground colour; if the
+ * ForegroundSpecified bit wasn't set then the foreground is the same as
+ * the last tile.
+ *
+ * The position and size of each subrectangle is specified in two bytes. The
+ * Pack macros below can be used to generate the two bytes from x, y, w, h,
+ * and the Extract macros can be used to extract the x, y, w, h values from
+ * the two bytes.
+ */
+
+#define rfbHextileRaw (1 << 0)
+#define rfbHextileBackgroundSpecified (1 << 1)
+#define rfbHextileForegroundSpecified (1 << 2)
+#define rfbHextileAnySubrects (1 << 3)
+#define rfbHextileSubrectsColoured (1 << 4)
+
+#define rfbHextilePackXY(x,y) (((x) << 4) | (y))
+#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1))
+#define rfbHextileExtractX(byte) ((byte) >> 4)
+#define rfbHextileExtractY(byte) ((byte) & 0xf)
+#define rfbHextileExtractW(byte) (((byte) >> 4) + 1)
+#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1)
+
+
+/*-----------------------------------------------------------------------------
+ * SetColourMapEntries - these messages are only sent if the pixel
+ * format uses a "colour map" (i.e. trueColour false) and the client has not
+ * fixed the entire colour map using FixColourMapEntries. In addition they
+ * will only start being sent after the client has sent its first
+ * FramebufferUpdateRequest. So if the client always tells the server to use
+ * trueColour then it never needs to process this type of message.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbSetColourMapEntriesMsg;
+
+#define sz_rfbSetColourMapEntriesMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * Bell - ring a bell on the client if it has one.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbBell */
+} rfbBellMsg;
+
+#define sz_rfbBellMsg 1
+
+
+
+/*-----------------------------------------------------------------------------
+ * ServerCutText - the server has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbServerCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbServerCutTextMsg;
+
+#define sz_rfbServerCutTextMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all server->client messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbFramebufferUpdateMsg fu;
+ rfbSetColourMapEntriesMsg scme;
+ rfbBellMsg b;
+ rfbServerCutTextMsg sct;
+} rfbServerToClientMsg;
+
+
+
+/*****************************************************************************
+ *
+ * Message definitions (client -> server)
+ *
+ *****************************************************************************/
+
+
+/*-----------------------------------------------------------------------------
+ * SetPixelFormat - tell the RFB server the format in which the client wants
+ * pixels sent.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetPixelFormat */
+ CARD8 pad1;
+ CARD16 pad2;
+ rfbPixelFormat format;
+} rfbSetPixelFormatMsg;
+
+#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4)
+
+
+/*-----------------------------------------------------------------------------
+ * FixColourMapEntries - when the pixel format uses a "colour map", fix
+ * read-only colour map entries.
+ *
+ * ***************** NOT CURRENTLY SUPPORTED *****************
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFixColourMapEntries */
+ CARD8 pad;
+ CARD16 firstColour;
+ CARD16 nColours;
+
+ /* Followed by nColours * 3 * CARD16
+ r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */
+
+} rfbFixColourMapEntriesMsg;
+
+#define sz_rfbFixColourMapEntriesMsg 6
+
+
+/*-----------------------------------------------------------------------------
+ * SetEncodings - tell the RFB server which encoding types we accept. Put them
+ * in order of preference, if we have any. We may always receive raw
+ * encoding, even if we don't specify it here.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbSetEncodings */
+ CARD8 pad;
+ CARD16 nEncodings;
+ /* followed by nEncodings * CARD32 encoding types */
+} rfbSetEncodingsMsg;
+
+#define sz_rfbSetEncodingsMsg 4
+
+
+/*-----------------------------------------------------------------------------
+ * FramebufferUpdateRequest - request for a framebuffer update. If incremental
+ * is true then the client just wants the changes since the last update. If
+ * false then it wants the whole of the specified rectangle.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbFramebufferUpdateRequest */
+ CARD8 incremental;
+ CARD16 x;
+ CARD16 y;
+ CARD16 w;
+ CARD16 h;
+} rfbFramebufferUpdateRequestMsg;
+
+#define sz_rfbFramebufferUpdateRequestMsg 10
+
+
+/*-----------------------------------------------------------------------------
+ * KeyEvent - key press or release
+ *
+ * Keys are specified using the "keysym" values defined by the X Window System.
+ * For most ordinary keys, the keysym is the same as the corresponding ASCII
+ * value. Other common keys are:
+ *
+ * BackSpace 0xff08
+ * Tab 0xff09
+ * Return or Enter 0xff0d
+ * Escape 0xff1b
+ * Insert 0xff63
+ * Delete 0xffff
+ * Home 0xff50
+ * End 0xff57
+ * Page Up 0xff55
+ * Page Down 0xff56
+ * Left 0xff51
+ * Up 0xff52
+ * Right 0xff53
+ * Down 0xff54
+ * F1 0xffbe
+ * F2 0xffbf
+ * ... ...
+ * F12 0xffc9
+ * Shift 0xffe1
+ * Control 0xffe3
+ * Meta 0xffe7
+ * Alt 0xffe9
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbKeyEvent */
+ CARD8 down; /* true if down (press), false if up */
+ CARD16 pad;
+ CARD32 key; /* key is specified as an X keysym */
+} rfbKeyEventMsg;
+
+#define sz_rfbKeyEventMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * PointerEvent - mouse/pen move and/or button press.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbPointerEvent */
+ CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */
+ CARD16 x;
+ CARD16 y;
+} rfbPointerEventMsg;
+
+#define rfbButton1Mask 1
+#define rfbButton2Mask 2
+#define rfbButton3Mask 4
+
+#define sz_rfbPointerEventMsg 6
+
+
+
+/*-----------------------------------------------------------------------------
+ * ClientCutText - the client has new text in its cut buffer.
+ */
+
+typedef struct {
+ CARD8 type; /* always rfbClientCutText */
+ CARD8 pad1;
+ CARD16 pad2;
+ CARD32 length;
+ /* followed by char text[length] */
+} rfbClientCutTextMsg;
+
+#define sz_rfbClientCutTextMsg 8
+
+
+
+/*-----------------------------------------------------------------------------
+ * Union of all client->server messages.
+ */
+
+typedef union {
+ CARD8 type;
+ rfbSetPixelFormatMsg spf;
+ rfbFixColourMapEntriesMsg fcme;
+ rfbSetEncodingsMsg se;
+ rfbFramebufferUpdateRequestMsg fur;
+ rfbKeyEventMsg ke;
+ rfbPointerEventMsg pe;
+ rfbClientCutTextMsg cct;
+} rfbClientToServerMsg;
diff --git a/tools/ioemu/gui/sdl.h b/tools/ioemu/gui/sdl.h
new file mode 100644
index 0000000000..c8df029f05
--- /dev/null
+++ b/tools/ioemu/gui/sdl.h
@@ -0,0 +1,1038 @@
+#define BX_HEADERBAR_FG_RED 0x10
+#define BX_HEADERBAR_FG_GREEN 0x10
+#define BX_HEADERBAR_FG_BLUE 0x10
+#define BX_HEADERBAR_BG_RED 0xD0
+#define BX_HEADERBAR_BG_GREEN 0xD0
+#define BX_HEADERBAR_BG_BLUE 0xD0
+
+unsigned char sdl_font8x16[256][16] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 0
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 126, 129, 165, 129, 129, 189, // 1
+ 153, 129, 129, 126, 0, 0, 0, 0 },
+ { 0, 0, 126, 255, 219, 255, 255, 195, // 2
+ 231, 255, 255, 126, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 108, 254, 254, 254, // 3
+ 254, 124, 56, 16, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 16, 56, 124, 254, // 4
+ 124, 56, 16, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 24, 60, 60, 231, 231, // 5
+ 231, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 0, 0, 24, 60, 126, 255, 255, // 6
+ 126, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 24, 60, // 7
+ 60, 24, 0, 0, 0, 0, 0, 0 },
+ { 255, 255, 255, 255, 255, 255, 231, 195, // 8
+ 195, 231, 255, 255, 255, 255, 255, 255 },
+ { 0, 0, 0, 0, 0, 60, 102, 66, // 9
+ 66, 102, 60, 0, 0, 0, 0, 0 },
+ { 255, 255, 255, 255, 255, 195, 153, 189, // 10
+ 189, 153, 195, 255, 255, 255, 255, 255 },
+ { 0, 0, 30, 14, 26, 50, 120, 204, // 11
+ 204, 204, 204, 120, 0, 0, 0, 0 },
+ { 0, 0, 60, 102, 102, 102, 102, 60, // 12
+ 24, 126, 24, 24, 0, 0, 0, 0 },
+ { 0, 0, 63, 51, 63, 48, 48, 48, // 13
+ 48, 112, 240, 224, 0, 0, 0, 0 },
+ { 0, 0, 127, 99, 127, 99, 99, 99, // 14
+ 99, 103, 231, 230, 192, 0, 0, 0 },
+ { 0, 0, 0, 24, 24, 219, 60, 231, // 15
+ 60, 219, 24, 24, 0, 0, 0, 0 },
+ { 0, 128, 192, 224, 240, 248, 254, 248, // 16
+ 240, 224, 192, 128, 0, 0, 0, 0 },
+ { 0, 2, 6, 14, 30, 62, 254, 62, // 17
+ 30, 14, 6, 2, 0, 0, 0, 0 },
+ { 0, 0, 24, 60, 126, 24, 24, 24, // 18
+ 126, 60, 24, 0, 0, 0, 0, 0 },
+ { 0, 0, 102, 102, 102, 102, 102, 102, // 19
+ 102, 0, 102, 102, 0, 0, 0, 0 },
+ { 0, 0, 127, 219, 219, 219, 123, 27, // 20
+ 27, 27, 27, 27, 0, 0, 0, 0 },
+ { 0, 124, 198, 96, 56, 108, 198, 198, // 21
+ 108, 56, 12, 198, 124, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 22
+ 254, 254, 254, 254, 0, 0, 0, 0 },
+ { 0, 0, 24, 60, 126, 24, 24, 24, // 23
+ 126, 60, 24, 126, 0, 0, 0, 0 },
+ { 0, 0, 24, 60, 126, 24, 24, 24, // 24
+ 24, 24, 24, 24, 0, 0, 0, 0 },
+ { 0, 0, 24, 24, 24, 24, 24, 24, // 25
+ 24, 126, 60, 24, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 24, 12, 254, // 26
+ 12, 24, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 48, 96, 254, // 27
+ 96, 48, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 192, 192, // 28
+ 192, 254, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 40, 108, 254, // 29
+ 108, 40, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 16, 56, 56, 124, // 30
+ 124, 254, 254, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 254, 254, 124, 124, // 31
+ 56, 56, 16, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 32
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 24, 60, 60, 60, 24, 24, // 33
+ 24, 0, 24, 24, 0, 0, 0, 0 },
+ { 0, 102, 102, 102, 36, 0, 0, 0, // 34
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 108, 108, 254, 108, 108, // 35
+ 108, 254, 108, 108, 0, 0, 0, 0 },
+ { 24, 24, 124, 198, 194, 192, 124, 6, // 36
+ 6, 134, 198, 124, 24, 24, 0, 0 },
+ { 0, 0, 0, 0, 194, 198, 12, 24, // 37
+ 48, 96, 198, 134, 0, 0, 0, 0 },
+ { 0, 0, 56, 108, 108, 56, 118, 220, // 38
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 48, 48, 48, 96, 0, 0, 0, // 39
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 12, 24, 48, 48, 48, 48, // 40
+ 48, 48, 24, 12, 0, 0, 0, 0 },
+ { 0, 0, 48, 24, 12, 12, 12, 12, // 41
+ 12, 12, 24, 48, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 102, 60, 255, // 42
+ 60, 102, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 24, 24, 126, // 43
+ 24, 24, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 44
+ 0, 24, 24, 24, 48, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 254, // 45
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 46
+ 0, 0, 24, 24, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 2, 6, 12, 24, // 47
+ 48, 96, 192, 128, 0, 0, 0, 0 },
+ { 0, 0, 56, 108, 198, 198, 214, 214, // 48
+ 198, 198, 108, 56, 0, 0, 0, 0 },
+ { 0, 0, 24, 56, 120, 24, 24, 24, // 49
+ 24, 24, 24, 126, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 6, 12, 24, 48, // 50
+ 96, 192, 198, 254, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 6, 6, 60, 6, // 51
+ 6, 6, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 12, 28, 60, 108, 204, 254, // 52
+ 12, 12, 12, 30, 0, 0, 0, 0 },
+ { 0, 0, 254, 192, 192, 192, 252, 6, // 53
+ 6, 6, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 56, 96, 192, 192, 252, 198, // 54
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 254, 198, 6, 6, 12, 24, // 55
+ 48, 48, 48, 48, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 198, 198, 124, 198, // 56
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 198, 198, 126, 6, // 57
+ 6, 6, 12, 120, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 24, 24, 0, 0, // 58
+ 0, 24, 24, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 24, 24, 0, 0, // 59
+ 0, 24, 24, 48, 0, 0, 0, 0 },
+ { 0, 0, 0, 6, 12, 24, 48, 96, // 60
+ 48, 24, 12, 6, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 126, 0, 0, // 61
+ 126, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 96, 48, 24, 12, 6, // 62
+ 12, 24, 48, 96, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 198, 12, 24, 24, // 63
+ 24, 0, 24, 24, 0, 0, 0, 0 },
+ { 0, 0, 0, 124, 198, 198, 222, 222, // 64
+ 222, 220, 192, 124, 0, 0, 0, 0 },
+ { 0, 0, 16, 56, 108, 198, 198, 254, // 65
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 0, 0, 252, 102, 102, 102, 124, 102, // 66
+ 102, 102, 102, 252, 0, 0, 0, 0 },
+ { 0, 0, 60, 102, 194, 192, 192, 192, // 67
+ 192, 194, 102, 60, 0, 0, 0, 0 },
+ { 0, 0, 248, 108, 102, 102, 102, 102, // 68
+ 102, 102, 108, 248, 0, 0, 0, 0 },
+ { 0, 0, 254, 102, 98, 104, 120, 104, // 69
+ 96, 98, 102, 254, 0, 0, 0, 0 },
+ { 0, 0, 254, 102, 98, 104, 120, 104, // 70
+ 96, 96, 96, 240, 0, 0, 0, 0 },
+ { 0, 0, 60, 102, 194, 192, 192, 222, // 71
+ 198, 198, 102, 58, 0, 0, 0, 0 },
+ { 0, 0, 198, 198, 198, 198, 254, 198, // 72
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 0, 0, 60, 24, 24, 24, 24, 24, // 73
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 0, 30, 12, 12, 12, 12, 12, // 74
+ 204, 204, 204, 120, 0, 0, 0, 0 },
+ { 0, 0, 230, 102, 102, 108, 120, 120, // 75
+ 108, 102, 102, 230, 0, 0, 0, 0 },
+ { 0, 0, 240, 96, 96, 96, 96, 96, // 76
+ 96, 98, 102, 254, 0, 0, 0, 0 },
+ { 0, 0, 198, 238, 254, 254, 214, 198, // 77
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 0, 0, 198, 230, 246, 254, 222, 206, // 78
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 198, 198, 198, 198, // 79
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 252, 102, 102, 102, 124, 96, // 80
+ 96, 96, 96, 240, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 198, 198, 198, 198, // 81
+ 198, 214, 222, 124, 12, 14, 0, 0 },
+ { 0, 0, 252, 102, 102, 102, 124, 108, // 82
+ 102, 102, 102, 230, 0, 0, 0, 0 },
+ { 0, 0, 124, 198, 198, 96, 56, 12, // 83
+ 6, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 126, 126, 90, 24, 24, 24, // 84
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 0, 198, 198, 198, 198, 198, 198, // 85
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 198, 198, 198, 198, 198, 198, // 86
+ 198, 108, 56, 16, 0, 0, 0, 0 },
+ { 0, 0, 198, 198, 198, 198, 214, 214, // 87
+ 214, 254, 238, 108, 0, 0, 0, 0 },
+ { 0, 0, 198, 198, 108, 124, 56, 56, // 88
+ 124, 108, 198, 198, 0, 0, 0, 0 },
+ { 0, 0, 102, 102, 102, 102, 60, 24, // 89
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 0, 254, 198, 134, 12, 24, 48, // 90
+ 96, 194, 198, 254, 0, 0, 0, 0 },
+ { 0, 0, 60, 48, 48, 48, 48, 48, // 91
+ 48, 48, 48, 60, 0, 0, 0, 0 },
+ { 0, 0, 0, 128, 192, 224, 112, 56, // 92
+ 28, 14, 6, 2, 0, 0, 0, 0 },
+ { 0, 0, 60, 12, 12, 12, 12, 12, // 93
+ 12, 12, 12, 60, 0, 0, 0, 0 },
+ { 16, 56, 108, 198, 0, 0, 0, 0, // 94
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 95
+ 0, 0, 0, 0, 0, 255, 0, 0 },
+ { 0, 48, 24, 12, 0, 0, 0, 0, // 96
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 120, 12, 124, // 97
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 0, 224, 96, 96, 120, 108, 102, // 98
+ 102, 102, 102, 124, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 124, 198, 192, // 99
+ 192, 192, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 28, 12, 12, 60, 108, 204, // 100
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 124, 198, 254, // 101
+ 192, 192, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 28, 54, 50, 48, 120, 48, // 102
+ 48, 48, 48, 120, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 118, 204, 204, // 103
+ 204, 204, 204, 124, 12, 204, 120, 0 },
+ { 0, 0, 224, 96, 96, 108, 118, 102, // 104
+ 102, 102, 102, 230, 0, 0, 0, 0 },
+ { 0, 0, 24, 24, 0, 56, 24, 24, // 105
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 0, 6, 6, 0, 14, 6, 6, // 106
+ 6, 6, 6, 6, 102, 102, 60, 0 },
+ { 0, 0, 224, 96, 96, 102, 108, 120, // 107
+ 120, 108, 102, 230, 0, 0, 0, 0 },
+ { 0, 0, 56, 24, 24, 24, 24, 24, // 108
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 236, 254, 214, // 109
+ 214, 214, 214, 198, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 220, 102, 102, // 110
+ 102, 102, 102, 102, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 124, 198, 198, // 111
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 220, 102, 102, // 112
+ 102, 102, 102, 124, 96, 96, 240, 0 },
+ { 0, 0, 0, 0, 0, 118, 204, 204, // 113
+ 204, 204, 204, 124, 12, 12, 30, 0 },
+ { 0, 0, 0, 0, 0, 220, 118, 102, // 114
+ 96, 96, 96, 240, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 124, 198, 96, // 115
+ 56, 12, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 16, 48, 48, 252, 48, 48, // 116
+ 48, 48, 54, 28, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 204, 204, 204, // 117
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 198, 198, 198, // 118
+ 198, 198, 108, 56, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 198, 198, 214, // 119
+ 214, 214, 254, 108, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 198, 108, 56, // 120
+ 56, 56, 108, 198, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 198, 198, 198, // 121
+ 198, 198, 198, 126, 6, 12, 248, 0 },
+ { 0, 0, 0, 0, 0, 254, 204, 24, // 122
+ 48, 96, 198, 254, 0, 0, 0, 0 },
+ { 0, 0, 14, 24, 24, 24, 112, 24, // 123
+ 24, 24, 24, 14, 0, 0, 0, 0 },
+ { 0, 0, 24, 24, 24, 24, 24, 24, // 124
+ 24, 24, 24, 24, 0, 0, 0, 0 },
+ { 0, 0, 112, 24, 24, 24, 14, 24, // 125
+ 24, 24, 24, 112, 0, 0, 0, 0 },
+ { 0, 118, 220, 0, 0, 0, 0, 0, // 126
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 16, 56, 108, 198, // 127
+ 198, 198, 254, 0, 0, 0, 0, 0 },
+ { 0, 0, 60, 102, 194, 192, 192, 192, // 128
+ 192, 194, 102, 60, 24, 112, 0, 0 },
+ { 0, 0, 204, 0, 0, 204, 204, 204, // 129
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 12, 24, 48, 0, 124, 198, 254, // 130
+ 192, 192, 198, 124, 0, 0, 0, 0 },
+ { 0, 16, 56, 108, 0, 120, 12, 124, // 131
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 0, 204, 0, 0, 120, 12, 124, // 132
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 96, 48, 24, 0, 120, 12, 124, // 133
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 56, 108, 56, 0, 120, 12, 124, // 134
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 124, 198, 192, // 135
+ 192, 192, 198, 124, 24, 112, 0, 0 },
+ { 0, 16, 56, 108, 0, 124, 198, 254, // 136
+ 192, 192, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 198, 0, 0, 124, 198, 254, // 137
+ 192, 192, 198, 124, 0, 0, 0, 0 },
+ { 0, 96, 48, 24, 0, 124, 198, 254, // 138
+ 192, 192, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 102, 0, 0, 56, 24, 24, // 139
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 24, 60, 102, 0, 56, 24, 24, // 140
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 96, 48, 24, 0, 56, 24, 24, // 141
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 198, 0, 16, 56, 108, 198, 198, // 142
+ 254, 198, 198, 198, 0, 0, 0, 0 },
+ { 56, 108, 56, 16, 56, 108, 198, 198, // 143
+ 254, 198, 198, 198, 0, 0, 0, 0 },
+ { 12, 24, 0, 254, 102, 98, 104, 120, // 144
+ 104, 98, 102, 254, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 236, 54, 54, // 145
+ 126, 216, 216, 110, 0, 0, 0, 0 },
+ { 0, 0, 62, 108, 204, 204, 254, 204, // 146
+ 204, 204, 204, 206, 0, 0, 0, 0 },
+ { 0, 16, 56, 108, 0, 124, 198, 198, // 147
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 198, 0, 0, 124, 198, 198, // 148
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 96, 48, 24, 0, 124, 198, 198, // 149
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 48, 120, 204, 0, 204, 204, 204, // 150
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 96, 48, 24, 0, 204, 204, 204, // 151
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 0, 198, 0, 0, 198, 198, 198, // 152
+ 198, 198, 198, 126, 6, 12, 120, 0 },
+ { 0, 198, 0, 124, 198, 198, 198, 198, // 153
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 198, 0, 198, 198, 198, 198, 198, // 154
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 124, 206, 222, // 155
+ 246, 230, 198, 124, 0, 0, 0, 0 },
+ { 0, 56, 108, 100, 96, 240, 96, 96, // 156
+ 96, 96, 230, 252, 0, 0, 0, 0 },
+ { 0, 4, 124, 206, 206, 214, 214, 214, // 157
+ 214, 230, 230, 124, 64, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 198, 108, 56, // 158
+ 56, 108, 198, 0, 0, 0, 0, 0 },
+ { 0, 14, 27, 24, 24, 24, 126, 24, // 159
+ 24, 24, 216, 112, 0, 0, 0, 0 },
+ { 0, 24, 48, 96, 0, 120, 12, 124, // 160
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 12, 24, 48, 0, 56, 24, 24, // 161
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 24, 48, 96, 0, 124, 198, 198, // 162
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 24, 48, 96, 0, 204, 204, 204, // 163
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 0, 0, 118, 220, 0, 220, 102, 102, // 164
+ 102, 102, 102, 102, 0, 0, 0, 0 },
+ { 118, 220, 0, 198, 230, 246, 254, 222, // 165
+ 206, 198, 198, 198, 0, 0, 0, 0 },
+ { 0, 0, 60, 108, 108, 62, 0, 126, // 166
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 56, 108, 108, 56, 0, 124, // 167
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 48, 48, 0, 48, 48, 96, // 168
+ 192, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 124, 130, 178, 170, 178, 170, // 169
+ 170, 130, 124, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 254, 6, // 170
+ 6, 6, 6, 0, 0, 0, 0, 0 },
+ { 0, 96, 224, 98, 102, 108, 24, 48, // 171
+ 96, 220, 134, 12, 24, 62, 0, 0 },
+ { 0, 96, 224, 98, 102, 108, 24, 48, // 172
+ 102, 206, 154, 63, 6, 6, 0, 0 },
+ { 0, 0, 24, 24, 0, 24, 24, 24, // 173
+ 60, 60, 60, 24, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 54, 108, 216, // 174
+ 108, 54, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 216, 108, 54, // 175
+ 108, 216, 0, 0, 0, 0, 0, 0 },
+ { 17, 68, 17, 68, 17, 68, 17, 68, // 176
+ 17, 68, 17, 68, 17, 68, 17, 68 },
+ { 85, 170, 85, 170, 85, 170, 85, 170, // 177
+ 85, 170, 85, 170, 85, 170, 85, 170 },
+ { 221, 119, 221, 119, 221, 119, 221, 119, // 178
+ 221, 119, 221, 119, 221, 119, 221, 119 },
+ { 24, 24, 24, 24, 24, 24, 24, 24, // 179
+ 24, 24, 24, 24, 24, 24, 24, 24 },
+ { 24, 24, 24, 24, 24, 24, 24, 248, // 180
+ 24, 24, 24, 24, 24, 24, 24, 24 },
+ { 96, 192, 16, 56, 108, 198, 198, 254, // 181
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 124, 198, 16, 56, 108, 198, 198, 254, // 182
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 12, 6, 16, 56, 108, 198, 198, 254, // 183
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 0, 0, 124, 130, 154, 162, 162, 162, // 184
+ 154, 130, 124, 0, 0, 0, 0, 0 },
+ { 54, 54, 54, 54, 54, 246, 6, 246, // 185
+ 54, 54, 54, 54, 54, 54, 54, 54 },
+ { 54, 54, 54, 54, 54, 54, 54, 54, // 186
+ 54, 54, 54, 54, 54, 54, 54, 54 },
+ { 0, 0, 0, 0, 0, 254, 6, 246, // 187
+ 54, 54, 54, 54, 54, 54, 54, 54 },
+ { 54, 54, 54, 54, 54, 246, 6, 254, // 188
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 24, 24, 124, 198, 192, 192, // 189
+ 198, 124, 24, 24, 0, 0, 0, 0 },
+ { 0, 0, 0, 102, 102, 60, 24, 126, // 190
+ 24, 126, 24, 24, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 248, // 191
+ 24, 24, 24, 24, 24, 24, 24, 24 },
+ { 24, 24, 24, 24, 24, 24, 24, 31, // 192
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 24, 24, 24, 24, 24, 24, 24, 255, // 193
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 255, // 194
+ 24, 24, 24, 24, 24, 24, 24, 24 },
+ { 24, 24, 24, 24, 24, 24, 24, 31, // 195
+ 24, 24, 24, 24, 24, 24, 24, 24 },
+ { 0, 0, 0, 0, 0, 0, 0, 255, // 196
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 24, 24, 24, 24, 24, 24, 24, 255, // 197
+ 24, 24, 24, 24, 24, 24, 24, 24 },
+ { 0, 0, 118, 220, 0, 120, 12, 124, // 198
+ 204, 204, 204, 118, 0, 0, 0, 0 },
+ { 118, 220, 0, 56, 108, 198, 198, 254, // 199
+ 198, 198, 198, 198, 0, 0, 0, 0 },
+ { 54, 54, 54, 54, 54, 55, 48, 63, // 200
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 63, 48, 55, // 201
+ 54, 54, 54, 54, 54, 54, 54, 54 },
+ { 54, 54, 54, 54, 54, 247, 0, 255, // 202
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 255, 0, 247, // 203
+ 54, 54, 54, 54, 54, 54, 54, 54 },
+ { 54, 54, 54, 54, 54, 55, 48, 55, // 204
+ 54, 54, 54, 54, 54, 54, 54, 54 },
+ { 0, 0, 0, 0, 0, 255, 0, 255, // 205
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 54, 54, 54, 54, 54, 247, 0, 247, // 206
+ 54, 54, 54, 54, 54, 54, 54, 54 },
+ { 0, 0, 0, 0, 198, 124, 198, 198, // 207
+ 198, 198, 124, 198, 0, 0, 0, 0 },
+ { 0, 0, 52, 24, 44, 6, 62, 102, // 208
+ 102, 102, 102, 60, 0, 0, 0, 0 },
+ { 0, 0, 248, 108, 102, 102, 246, 102, // 209
+ 102, 102, 108, 248, 0, 0, 0, 0 },
+ { 56, 108, 0, 254, 102, 98, 104, 120, // 210
+ 104, 98, 102, 254, 0, 0, 0, 0 },
+ { 0, 198, 0, 254, 102, 98, 104, 120, // 211
+ 104, 98, 102, 254, 0, 0, 0, 0 },
+ { 48, 24, 0, 254, 102, 98, 104, 120, // 212
+ 104, 98, 102, 254, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 56, 24, 24, // 213
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 12, 24, 0, 60, 24, 24, 24, 24, // 214
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 60, 102, 0, 60, 24, 24, 24, 24, // 215
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 102, 0, 60, 24, 24, 24, 24, // 216
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 24, 24, 24, 24, 24, 24, 24, 248, // 217
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 31, // 218
+ 24, 24, 24, 24, 24, 24, 24, 24 },
+ { 255, 255, 255, 255, 255, 255, 255, 255, // 219
+ 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 0, 0, 0, 0, 0, 0, 0, 255, // 220
+ 255, 255, 255, 255, 255, 255, 255, 255 },
+ { 0, 24, 24, 24, 24, 24, 0, 0, // 221
+ 24, 24, 24, 24, 24, 0, 0, 0 },
+ { 48, 24, 0, 60, 24, 24, 24, 24, // 222
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 255, 255, 255, 255, 255, 255, 255, 0, // 223
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 24, 48, 0, 124, 198, 198, 198, 198, // 224
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 120, 204, 204, 204, 216, 204, // 225
+ 198, 198, 198, 204, 0, 0, 0, 0 },
+ { 56, 108, 0, 124, 198, 198, 198, 198, // 226
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 48, 24, 0, 124, 198, 198, 198, 198, // 227
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 118, 220, 0, 124, 198, 198, // 228
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 118, 220, 0, 124, 198, 198, 198, 198, // 229
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 102, 102, 102, // 230
+ 102, 102, 102, 124, 96, 96, 192, 0 },
+ { 0, 0, 224, 96, 96, 124, 102, 102, // 231
+ 102, 102, 102, 124, 96, 96, 240, 0 },
+ { 0, 0, 240, 96, 124, 102, 102, 102, // 232
+ 102, 124, 96, 240, 0, 0, 0, 0 },
+ { 24, 48, 0, 198, 198, 198, 198, 198, // 233
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 56, 108, 0, 198, 198, 198, 198, 198, // 234
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 48, 24, 0, 198, 198, 198, 198, 198, // 235
+ 198, 198, 198, 124, 0, 0, 0, 0 },
+ { 0, 12, 24, 48, 0, 198, 198, 198, // 236
+ 198, 198, 198, 126, 6, 12, 248, 0 },
+ { 12, 24, 0, 102, 102, 102, 102, 60, // 237
+ 24, 24, 24, 60, 0, 0, 0, 0 },
+ { 0, 255, 0, 0, 0, 0, 0, 0, // 238
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 12, 24, 48, 0, 0, 0, 0, // 239
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 254, // 240
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 24, 24, 126, 24, // 241
+ 24, 0, 0, 126, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 242
+ 0, 0, 0, 0, 255, 0, 255, 0 },
+ { 0, 224, 48, 98, 54, 236, 24, 48, // 243
+ 102, 206, 154, 63, 6, 6, 0, 0 },
+ { 0, 0, 127, 219, 219, 219, 123, 27, // 244
+ 27, 27, 27, 27, 0, 0, 0, 0 },
+ { 0, 124, 198, 96, 56, 108, 198, 198, // 245
+ 108, 56, 12, 198, 124, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 24, 0, 126, // 246
+ 0, 24, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 247
+ 0, 0, 0, 24, 12, 120, 0, 0 },
+ { 0, 56, 108, 108, 56, 0, 0, 0, // 248
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 198, 0, 0, 0, 0, 0, 0, // 249
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 24, // 250
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 24, 56, 24, 24, 24, 60, 0, // 251
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 124, 6, 60, 6, 6, 124, 0, // 252
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 60, 102, 12, 24, 50, 126, 0, // 253
+ 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 126, 126, 126, 126, // 254
+ 126, 126, 126, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, // 255
+ 0, 0, 0, 0, 0, 0, 0, 0 }};
+
+unsigned char sdl_font8x8[256][8] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // 0
+ { 126, 129, 165, 129, 189, 153, 129, 126 }, // 1
+ { 126, 255, 219, 255, 195, 231, 255, 126 }, // 2
+ { 108, 254, 254, 254, 124, 56, 16, 0 }, // 3
+ { 16, 56, 124, 254, 124, 56, 16, 0 }, // 4
+ { 56, 124, 56, 254, 254, 214, 16, 56 }, // 5
+ { 16, 56, 124, 254, 254, 124, 16, 56 }, // 6
+ { 0, 0, 24, 60, 60, 24, 0, 0 }, // 7
+ { 255, 255, 231, 195, 195, 231, 255, 255 }, // 8
+ { 0, 60, 102, 66, 66, 102, 60, 0 }, // 9
+ { 255, 195, 153, 189, 189, 153, 195, 255 }, // 10
+ { 15, 7, 15, 125, 204, 204, 204, 120 }, // 11
+ { 60, 102, 102, 102, 60, 24, 126, 24 }, // 12
+ { 63, 51, 63, 48, 48, 112, 240, 224 }, // 13
+ { 127, 99, 127, 99, 99, 103, 230, 192 }, // 14
+ { 24, 219, 60, 231, 231, 60, 219, 24 }, // 15
+ { 128, 224, 248, 254, 248, 224, 128, 0 }, // 16
+ { 2, 14, 62, 254, 62, 14, 2, 0 }, // 17
+ { 24, 60, 126, 24, 24, 126, 60, 24 }, // 18
+ { 102, 102, 102, 102, 102, 0, 102, 0 }, // 19
+ { 127, 219, 219, 123, 27, 27, 27, 0 }, // 20
+ { 62, 97, 60, 102, 102, 60, 134, 124 }, // 21
+ { 0, 0, 0, 0, 126, 126, 126, 0 }, // 22
+ { 24, 60, 126, 24, 126, 60, 24, 255 }, // 23
+ { 24, 60, 126, 24, 24, 24, 24, 0 }, // 24
+ { 24, 24, 24, 24, 126, 60, 24, 0 }, // 25
+ { 0, 24, 12, 254, 12, 24, 0, 0 }, // 26
+ { 0, 48, 96, 254, 96, 48, 0, 0 }, // 27
+ { 0, 0, 192, 192, 192, 254, 0, 0 }, // 28
+ { 0, 36, 102, 255, 102, 36, 0, 0 }, // 29
+ { 0, 24, 60, 126, 255, 255, 0, 0 }, // 30
+ { 0, 255, 255, 126, 60, 24, 0, 0 }, // 31
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // 32
+ { 24, 60, 60, 24, 24, 0, 24, 0 }, // 33
+ { 102, 102, 36, 0, 0, 0, 0, 0 }, // 34
+ { 108, 108, 254, 108, 254, 108, 108, 0 }, // 35
+ { 24, 62, 96, 60, 6, 124, 24, 0 }, // 36
+ { 0, 198, 204, 24, 48, 102, 198, 0 }, // 37
+ { 56, 108, 56, 118, 220, 204, 118, 0 }, // 38
+ { 24, 24, 48, 0, 0, 0, 0, 0 }, // 39
+ { 12, 24, 48, 48, 48, 24, 12, 0 }, // 40
+ { 48, 24, 12, 12, 12, 24, 48, 0 }, // 41
+ { 0, 102, 60, 231, 60, 102, 0, 0 }, // 42
+ { 0, 24, 24, 126, 24, 24, 0, 0 }, // 43
+ { 0, 0, 0, 0, 0, 24, 24, 48 }, // 44
+ { 0, 0, 0, 126, 0, 0, 0, 0 }, // 45
+ { 0, 0, 0, 0, 0, 24, 24, 0 }, // 46
+ { 6, 12, 24, 48, 96, 192, 128, 0 }, // 47
+ { 124, 198, 206, 222, 246, 230, 124, 0 }, // 48
+ { 24, 56, 24, 24, 24, 24, 126, 0 }, // 49
+ { 124, 198, 6, 28, 48, 102, 254, 0 }, // 50
+ { 124, 198, 6, 60, 6, 198, 124, 0 }, // 51
+ { 28, 60, 108, 204, 254, 12, 30, 0 }, // 52
+ { 254, 192, 192, 252, 6, 198, 124, 0 }, // 53
+ { 56, 96, 192, 252, 198, 198, 124, 0 }, // 54
+ { 254, 198, 12, 24, 48, 48, 48, 0 }, // 55
+ { 124, 198, 198, 124, 198, 198, 124, 0 }, // 56
+ { 124, 198, 198, 126, 6, 12, 120, 0 }, // 57
+ { 0, 24, 24, 0, 0, 24, 24, 0 }, // 58
+ { 0, 24, 24, 0, 0, 24, 24, 48 }, // 59
+ { 6, 12, 24, 48, 24, 12, 6, 0 }, // 60
+ { 0, 0, 126, 0, 0, 126, 0, 0 }, // 61
+ { 96, 48, 24, 12, 24, 48, 96, 0 }, // 62
+ { 124, 198, 12, 24, 24, 0, 24, 0 }, // 63
+ { 124, 198, 222, 222, 222, 192, 120, 0 }, // 64
+ { 56, 108, 198, 254, 198, 198, 198, 0 }, // 65
+ { 252, 102, 102, 124, 102, 102, 252, 0 }, // 66
+ { 60, 102, 192, 192, 192, 102, 60, 0 }, // 67
+ { 248, 108, 102, 102, 102, 108, 248, 0 }, // 68
+ { 254, 98, 104, 120, 104, 98, 254, 0 }, // 69
+ { 254, 98, 104, 120, 104, 96, 240, 0 }, // 70
+ { 60, 102, 192, 192, 206, 102, 58, 0 }, // 71
+ { 198, 198, 198, 254, 198, 198, 198, 0 }, // 72
+ { 60, 24, 24, 24, 24, 24, 60, 0 }, // 73
+ { 30, 12, 12, 12, 204, 204, 120, 0 }, // 74
+ { 230, 102, 108, 120, 108, 102, 230, 0 }, // 75
+ { 240, 96, 96, 96, 98, 102, 254, 0 }, // 76
+ { 198, 238, 254, 254, 214, 198, 198, 0 }, // 77
+ { 198, 230, 246, 222, 206, 198, 198, 0 }, // 78
+ { 124, 198, 198, 198, 198, 198, 124, 0 }, // 79
+ { 252, 102, 102, 124, 96, 96, 240, 0 }, // 80
+ { 124, 198, 198, 198, 198, 206, 124, 14 }, // 81
+ { 252, 102, 102, 124, 108, 102, 230, 0 }, // 82
+ { 60, 102, 48, 24, 12, 102, 60, 0 }, // 83
+ { 126, 126, 90, 24, 24, 24, 60, 0 }, // 84
+ { 198, 198, 198, 198, 198, 198, 124, 0 }, // 85
+ { 198, 198, 198, 198, 198, 108, 56, 0 }, // 86
+ { 198, 198, 198, 214, 214, 254, 108, 0 }, // 87
+ { 198, 198, 108, 56, 108, 198, 198, 0 }, // 88
+ { 102, 102, 102, 60, 24, 24, 60, 0 }, // 89
+ { 254, 198, 140, 24, 50, 102, 254, 0 }, // 90
+ { 60, 48, 48, 48, 48, 48, 60, 0 }, // 91
+ { 192, 96, 48, 24, 12, 6, 2, 0 }, // 92
+ { 60, 12, 12, 12, 12, 12, 60, 0 }, // 93
+ { 16, 56, 108, 198, 0, 0, 0, 0 }, // 94
+ { 0, 0, 0, 0, 0, 0, 0, 255 }, // 95
+ { 48, 24, 12, 0, 0, 0, 0, 0 }, // 96
+ { 0, 0, 120, 12, 124, 204, 118, 0 }, // 97
+ { 224, 96, 124, 102, 102, 102, 220, 0 }, // 98
+ { 0, 0, 124, 198, 192, 198, 124, 0 }, // 99
+ { 28, 12, 124, 204, 204, 204, 118, 0 }, // 100
+ { 0, 0, 124, 198, 254, 192, 124, 0 }, // 101
+ { 60, 102, 96, 248, 96, 96, 240, 0 }, // 102
+ { 0, 0, 118, 204, 204, 124, 12, 248 }, // 103
+ { 224, 96, 108, 118, 102, 102, 230, 0 }, // 104
+ { 24, 0, 56, 24, 24, 24, 60, 0 }, // 105
+ { 6, 0, 6, 6, 6, 102, 102, 60 }, // 106
+ { 224, 96, 102, 108, 120, 108, 230, 0 }, // 107
+ { 56, 24, 24, 24, 24, 24, 60, 0 }, // 108
+ { 0, 0, 236, 254, 214, 214, 214, 0 }, // 109
+ { 0, 0, 220, 102, 102, 102, 102, 0 }, // 110
+ { 0, 0, 124, 198, 198, 198, 124, 0 }, // 111
+ { 0, 0, 220, 102, 102, 124, 96, 240 }, // 112
+ { 0, 0, 118, 204, 204, 124, 12, 30 }, // 113
+ { 0, 0, 220, 118, 96, 96, 240, 0 }, // 114
+ { 0, 0, 126, 192, 124, 6, 252, 0 }, // 115
+ { 48, 48, 252, 48, 48, 54, 28, 0 }, // 116
+ { 0, 0, 204, 204, 204, 204, 118, 0 }, // 117
+ { 0, 0, 198, 198, 198, 108, 56, 0 }, // 118
+ { 0, 0, 198, 214, 214, 254, 108, 0 }, // 119
+ { 0, 0, 198, 108, 56, 108, 198, 0 }, // 120
+ { 0, 0, 198, 198, 198, 126, 6, 252 }, // 121
+ { 0, 0, 126, 76, 24, 50, 126, 0 }, // 122
+ { 14, 24, 24, 112, 24, 24, 14, 0 }, // 123
+ { 24, 24, 24, 24, 24, 24, 24, 0 }, // 124
+ { 112, 24, 24, 14, 24, 24, 112, 0 }, // 125
+ { 118, 220, 0, 0, 0, 0, 0, 0 }, // 126
+ { 0, 16, 56, 108, 198, 198, 254, 0 }, // 127
+ { 124, 198, 192, 192, 198, 124, 12, 120 }, // 128
+ { 204, 0, 204, 204, 204, 204, 118, 0 }, // 129
+ { 12, 24, 124, 198, 254, 192, 124, 0 }, // 130
+ { 124, 130, 120, 12, 124, 204, 118, 0 }, // 131
+ { 198, 0, 120, 12, 124, 204, 118, 0 }, // 132
+ { 48, 24, 120, 12, 124, 204, 118, 0 }, // 133
+ { 48, 48, 120, 12, 124, 204, 118, 0 }, // 134
+ { 0, 0, 126, 192, 192, 126, 12, 56 }, // 135
+ { 124, 130, 124, 198, 254, 192, 124, 0 }, // 136
+ { 198, 0, 124, 198, 254, 192, 124, 0 }, // 137
+ { 48, 24, 124, 198, 254, 192, 124, 0 }, // 138
+ { 102, 0, 56, 24, 24, 24, 60, 0 }, // 139
+ { 124, 130, 56, 24, 24, 24, 60, 0 }, // 140
+ { 48, 24, 0, 56, 24, 24, 60, 0 }, // 141
+ { 198, 56, 108, 198, 254, 198, 198, 0 }, // 142
+ { 56, 108, 124, 198, 254, 198, 198, 0 }, // 143
+ { 24, 48, 254, 192, 248, 192, 254, 0 }, // 144
+ { 0, 0, 126, 18, 254, 144, 254, 0 }, // 145
+ { 62, 108, 204, 254, 204, 204, 206, 0 }, // 146
+ { 124, 130, 124, 198, 198, 198, 124, 0 }, // 147
+ { 198, 0, 124, 198, 198, 198, 124, 0 }, // 148
+ { 48, 24, 124, 198, 198, 198, 124, 0 }, // 149
+ { 120, 132, 0, 204, 204, 204, 118, 0 }, // 150
+ { 96, 48, 204, 204, 204, 204, 118, 0 }, // 151
+ { 198, 0, 198, 198, 198, 126, 6, 252 }, // 152
+ { 198, 56, 108, 198, 198, 108, 56, 0 }, // 153
+ { 198, 0, 198, 198, 198, 198, 124, 0 }, // 154
+ { 0, 2, 124, 206, 214, 230, 124, 128 }, // 155
+ { 56, 108, 100, 240, 96, 102, 252, 0 }, // 156
+ { 58, 108, 206, 214, 230, 108, 184, 0 }, // 157
+ { 0, 198, 108, 56, 108, 198, 0, 0 }, // 158
+ { 14, 27, 24, 60, 24, 216, 112, 0 }, // 159
+ { 24, 48, 120, 12, 124, 204, 118, 0 }, // 160
+ { 12, 24, 0, 56, 24, 24, 60, 0 }, // 161
+ { 12, 24, 124, 198, 198, 198, 124, 0 }, // 162
+ { 24, 48, 204, 204, 204, 204, 118, 0 }, // 163
+ { 118, 220, 0, 220, 102, 102, 102, 0 }, // 164
+ { 118, 220, 0, 230, 246, 222, 206, 0 }, // 165
+ { 60, 108, 108, 62, 0, 126, 0, 0 }, // 166
+ { 56, 108, 108, 56, 0, 124, 0, 0 }, // 167
+ { 24, 0, 24, 24, 48, 99, 62, 0 }, // 168
+ { 126, 129, 185, 165, 185, 165, 129, 126 }, // 169
+ { 0, 0, 0, 254, 6, 6, 0, 0 }, // 170
+ { 99, 230, 108, 126, 51, 102, 204, 15 }, // 171
+ { 99, 230, 108, 122, 54, 106, 223, 6 }, // 172
+ { 24, 0, 24, 24, 60, 60, 24, 0 }, // 173
+ { 0, 51, 102, 204, 102, 51, 0, 0 }, // 174
+ { 0, 204, 102, 51, 102, 204, 0, 0 }, // 175
+ { 34, 136, 34, 136, 34, 136, 34, 136 }, // 176
+ { 85, 170, 85, 170, 85, 170, 85, 170 }, // 177
+ { 119, 221, 119, 221, 119, 221, 119, 221 }, // 178
+ { 24, 24, 24, 24, 24, 24, 24, 24 }, // 179
+ { 24, 24, 56, 248, 56, 24, 24, 24 }, // 180
+ { 48, 96, 56, 108, 198, 254, 198, 0 }, // 181
+ { 124, 130, 56, 108, 198, 254, 198, 0 }, // 182
+ { 24, 12, 56, 108, 198, 254, 198, 0 }, // 183
+ { 126, 129, 157, 161, 161, 157, 129, 126 }, // 184
+ { 54, 54, 246, 6, 246, 54, 54, 54 }, // 185
+ { 54, 54, 54, 54, 54, 54, 54, 54 }, // 186
+ { 0, 0, 254, 6, 246, 54, 54, 54 }, // 187
+ { 54, 54, 246, 6, 254, 0, 0, 0 }, // 188
+ { 24, 24, 126, 192, 192, 126, 24, 24 }, // 189
+ { 102, 102, 60, 126, 24, 126, 24, 24 }, // 190
+ { 0, 0, 0, 240, 56, 24, 24, 24 }, // 191
+ { 24, 24, 28, 15, 0, 0, 0, 0 }, // 192
+ { 24, 24, 60, 255, 0, 0, 0, 0 }, // 193
+ { 0, 0, 0, 255, 60, 24, 24, 24 }, // 194
+ { 48, 48, 56, 63, 56, 48, 48, 48 }, // 195
+ { 0, 0, 0, 255, 0, 0, 0, 0 }, // 196
+ { 24, 24, 24, 60, 231, 60, 24, 24 }, // 197
+ { 240, 120, 120, 120, 60, 60, 60, 28 }, // 198
+ { 30, 60, 60, 60, 120, 120, 120, 112 }, // 199
+ { 15, 63, 63, 120, 120, 0, 1, 3 }, // 200
+ { 192, 224, 240, 240, 240, 240, 240, 224 }, // 201
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // 202
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // 203
+ { 30, 30, 14, 15, 15, 7, 7, 0 }, // 204
+ { 240, 240, 224, 224, 192, 192, 192, 0 }, // 205
+ { 6, 13, 27, 55, 47, 127, 126, 30 }, // 206
+ { 0, 252, 255, 255, 143, 119, 243, 3 }, // 207
+ { 0, 1, 7, 143, 143, 207, 207, 199 }, // 208
+ { 0, 248, 254, 254, 31, 15, 192, 248 }, // 209
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // 210
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // 211
+ { 30, 30, 30, 31, 15, 7, 7, 1 }, // 212
+ { 3, 3, 3, 7, 143, 255, 254, 252 }, // 213
+ { 195, 192, 192, 207, 143, 7, 7, 1 }, // 214
+ { 254, 255, 31, 15, 143, 254, 254, 248 }, // 215
+ { 102, 0, 60, 24, 24, 24, 60, 0 }, // 216
+ { 24, 24, 56, 240, 0, 0, 0, 0 }, // 217
+ { 0, 0, 0, 15, 28, 24, 24, 24 }, // 218
+ { 255, 255, 255, 255, 255, 255, 255, 255 }, // 219
+ { 0, 0, 0, 0, 255, 255, 255, 255 }, // 220
+ { 24, 24, 24, 0, 0, 24, 24, 24 }, // 221
+ { 48, 24, 60, 24, 24, 24, 60, 0 }, // 222
+ { 255, 255, 255, 255, 0, 0, 0, 0 }, // 223
+ { 0, 0, 0, 0, 0, 0, 0, 255 }, // 224
+ { 0, 0, 0, 0, 0, 255, 0, 255 }, // 225
+ { 0, 0, 0, 255, 0, 255, 0, 255 }, // 226
+ { 0, 255, 0, 255, 0, 255, 0, 255 }, // 227
+ { 0, 255, 0, 255, 0, 255, 0, 0 }, // 228
+ { 0, 255, 0, 255, 0, 0, 0, 0 }, // 229
+ { 0, 255, 0, 0, 0, 0, 0, 0 }, // 230
+ { 224, 128, 0, 0, 0, 0, 128, 224 }, // 231
+ { 248, 254, 255, 255, 255, 255, 254, 248 }, // 232
+ { 24, 48, 198, 198, 198, 198, 124, 0 }, // 233
+ { 124, 130, 0, 198, 198, 198, 124, 0 }, // 234
+ { 96, 48, 198, 198, 198, 198, 124, 0 }, // 235
+ { 24, 48, 198, 198, 198, 126, 6, 252 }, // 236
+ { 12, 24, 102, 102, 60, 24, 60, 0 }, // 237
+ { 255, 0, 0, 0, 0, 0, 0, 0 }, // 238
+ { 12, 24, 48, 0, 0, 0, 0, 0 }, // 239
+ { 0, 0, 0, 126, 0, 0, 0, 0 }, // 240
+ { 24, 24, 126, 24, 24, 0, 126, 0 }, // 241
+ { 0, 0, 0, 0, 0, 255, 0, 255 }, // 242
+ { 225, 50, 228, 58, 246, 42, 95, 134 }, // 243
+ { 127, 219, 219, 123, 27, 27, 27, 0 }, // 244
+ { 62, 97, 60, 102, 102, 60, 134, 124 }, // 245
+ { 0, 24, 0, 126, 0, 24, 0, 0 }, // 246
+ { 0, 0, 0, 0, 0, 24, 12, 56 }, // 247
+ { 56, 108, 108, 56, 0, 0, 0, 0 }, // 248
+ { 0, 198, 0, 0, 0, 0, 0, 0 }, // 249
+ { 0, 0, 0, 24, 0, 0, 0, 0 }, // 250
+ { 24, 56, 24, 24, 60, 0, 0, 0 }, // 251
+ { 120, 12, 56, 12, 120, 0, 0, 0 }, // 252
+ { 120, 12, 24, 48, 124, 0, 0, 0 }, // 253
+ { 0, 0, 60, 60, 60, 60, 0, 0 }, // 254
+ { 0, 0, 0, 0, 0, 0, 0, 0 }}; // 255
+
+/*
+unsigned char sdl_palette[256][3] = {
+ { 0, 0, 0 }, // 0
+ { 0, 0, 168 }, // 1
+ { 0, 168, 0 }, // 2
+ { 0, 168, 168 }, // 3
+ { 168, 0, 0 }, // 4
+ { 168, 0, 168 }, // 5
+ { 168, 84, 0 }, // 6
+ { 168, 168, 168 }, // 7
+ { 84, 84, 84 }, // 8
+ { 84, 84, 252 }, // 9
+ { 84, 252, 84 }, // 10
+ { 84, 252, 252 }, // 11
+ { 252, 84, 84 }, // 12
+ { 252, 84, 252 }, // 13
+ { 252, 252, 84 }, // 14
+ { 252, 252, 252 }, // 15
+ { 0, 0, 0 }, // 16
+ { 20, 20, 20 }, // 17
+ { 32, 32, 32 }, // 18
+ { 44, 44, 44 }, // 19
+ { 56, 56, 56 }, // 20
+ { 68, 68, 68 }, // 21
+ { 80, 80, 80 }, // 22
+ { 96, 96, 96 }, // 23
+ { 112, 112, 112 }, // 24
+ { 128, 128, 128 }, // 25
+ { 144, 144, 144 }, // 26
+ { 160, 160, 160 }, // 27
+ { 180, 180, 180 }, // 28
+ { 200, 200, 200 }, // 29
+ { 224, 224, 224 }, // 30
+ { 252, 252, 252 }, // 31
+ { 0, 0, 252 }, // 32
+ { 64, 0, 252 }, // 33
+ { 124, 0, 252 }, // 34
+ { 188, 0, 252 }, // 35
+ { 252, 0, 252 }, // 36
+ { 252, 0, 188 }, // 37
+ { 252, 0, 124 }, // 38
+ { 252, 0, 64 }, // 39
+ { 252, 0, 0 }, // 40
+ { 252, 64, 0 }, // 41
+ { 252, 124, 0 }, // 42
+ { 252, 188, 0 }, // 43
+ { 252, 252, 0 }, // 44
+ { 188, 252, 0 }, // 45
+ { 124, 252, 0 }, // 46
+ { 64, 252, 0 }, // 47
+ { 0, 252, 0 }, // 48
+ { 0, 252, 64 }, // 49
+ { 0, 252, 124 }, // 50
+ { 0, 252, 188 }, // 51
+ { 0, 252, 252 }, // 52
+ { 0, 188, 252 }, // 53
+ { 0, 124, 252 }, // 54
+ { 0, 64, 252 }, // 55
+ { 124, 124, 252 }, // 56
+ { 156, 124, 252 }, // 57
+ { 188, 124, 252 }, // 58
+ { 220, 124, 252 }, // 59
+ { 252, 124, 252 }, // 60
+ { 252, 124, 220 }, // 61
+ { 252, 124, 188 }, // 62
+ { 252, 124, 156 }, // 63
+ { 252, 124, 124 }, // 64
+ { 252, 156, 124 }, // 65
+ { 252, 188, 124 }, // 66
+ { 252, 220, 124 }, // 67
+ { 252, 252, 124 }, // 68
+ { 220, 252, 124 }, // 69
+ { 188, 252, 124 }, // 70
+ { 156, 252, 124 }, // 71
+ { 124, 252, 124 }, // 72
+ { 124, 252, 156 }, // 73
+ { 124, 252, 188 }, // 74
+ { 124, 252, 220 }, // 75
+ { 124, 252, 252 }, // 76
+ { 124, 220, 252 }, // 77
+ { 124, 188, 252 }, // 78
+ { 124, 156, 252 }, // 79
+ { 180, 180, 252 }, // 80
+ { 196, 180, 252 }, // 81
+ { 216, 180, 252 }, // 82
+ { 232, 180, 252 }, // 83
+ { 252, 180, 252 }, // 84
+ { 252, 180, 232 }, // 85
+ { 252, 180, 216 }, // 86
+ { 252, 180, 196 }, // 87
+ { 252, 180, 180 }, // 88
+ { 252, 196, 180 }, // 89
+ { 252, 216, 180 }, // 90
+ { 252, 232, 180 }, // 91
+ { 252, 252, 180 }, // 92
+ { 232, 252, 180 }, // 93
+ { 216, 252, 180 }, // 94
+ { 196, 252, 180 }, // 95
+ { 180, 252, 180 }, // 96
+ { 180, 252, 196 }, // 97
+ { 180, 252, 216 }, // 98
+ { 180, 252, 232 }, // 99
+ { 180, 252, 252 }, // 100
+ { 180, 232, 252 }, // 101
+ { 180, 216, 252 }, // 102
+ { 180, 196, 252 }, // 103
+ { 0, 0, 112 }, // 104
+ { 28, 0, 112 }, // 105
+ { 56, 0, 112 }, // 106
+ { 84, 0, 112 }, // 107
+ { 112, 0, 112 }, // 108
+ { 112, 0, 84 }, // 109
+ { 112, 0, 56 }, // 110
+ { 112, 0, 28 }, // 111
+ { 112, 0, 0 }, // 112
+ { 112, 28, 0 }, // 113
+ { 112, 56, 0 }, // 114
+ { 112, 84, 0 }, // 115
+ { 112, 112, 0 }, // 116
+ { 84, 112, 0 }, // 117
+ { 56, 112, 0 }, // 118
+ { 28, 112, 0 }, // 119
+ { 0, 112, 0 }, // 120
+ { 0, 112, 28 }, // 121
+ { 0, 112, 56 }, // 122
+ { 0, 112, 84 }, // 123
+ { 0, 112, 112 }, // 124
+ { 0, 84, 112 }, // 125
+ { 0, 56, 112 }, // 126
+ { 0, 28, 112 }, // 127
+ { 56, 56, 112 }, // 128
+ { 68, 56, 112 }, // 129
+ { 84, 56, 112 }, // 130
+ { 96, 56, 112 }, // 131
+ { 112, 56, 112 }, // 132
+ { 112, 56, 96 }, // 133
+ { 112, 56, 84 }, // 134
+ { 112, 56, 68 }, // 135
+ { 112, 56, 56 }, // 136
+ { 112, 68, 56 }, // 137
+ { 112, 84, 56 }, // 138
+ { 112, 96, 56 }, // 139
+ { 112, 112, 56 }, // 140
+ { 96, 112, 56 }, // 141
+ { 84, 112, 56 }, // 142
+ { 68, 112, 56 }, // 143
+ { 56, 112, 56 }, // 144
+ { 56, 112, 68 }, // 145
+ { 56, 112, 84 }, // 146
+ { 56, 112, 96 }, // 147
+ { 56, 112, 112 }, // 148
+ { 56, 96, 112 }, // 149
+ { 56, 84, 112 }, // 150
+ { 56, 68, 112 }, // 151
+ { 80, 80, 112 }, // 152
+ { 88, 80, 112 }, // 153
+ { 96, 80, 112 }, // 154
+ { 104, 80, 112 }, // 155
+ { 112, 80, 112 }, // 156
+ { 112, 80, 104 }, // 157
+ { 112, 80, 96 }, // 158
+ { 112, 80, 88 }, // 159
+ { 112, 80, 80 }, // 160
+ { 112, 88, 80 }, // 161
+ { 112, 96, 80 }, // 162
+ { 112, 104, 80 }, // 163
+ { 112, 112, 80 }, // 164
+ { 104, 112, 80 }, // 165
+ { 96, 112, 80 }, // 166
+ { 88, 112, 80 }, // 167
+ { 80, 112, 80 }, // 168
+ { 80, 112, 88 }, // 169
+ { 80, 112, 96 }, // 170
+ { 80, 112, 104 }, // 171
+ { 80, 112, 112 }, // 172
+ { 80, 104, 112 }, // 173
+ { 80, 96, 112 }, // 174
+ { 80, 88, 112 }, // 175
+ { 0, 0, 64 }, // 176
+ { 16, 0, 64 }, // 177
+ { 32, 0, 64 }, // 178
+ { 48, 0, 64 }, // 179
+ { 64, 0, 64 }, // 180
+ { 64, 0, 48 }, // 181
+ { 64, 0, 32 }, // 182
+ { 64, 0, 16 }, // 183
+ { 64, 0, 0 }, // 184
+ { 64, 16, 0 }, // 185
+ { 64, 32, 0 }, // 186
+ { 64, 48, 0 }, // 187
+ { 64, 64, 0 }, // 188
+ { 48, 64, 0 }, // 189
+ { 32, 64, 0 }, // 190
+ { 16, 64, 0 }, // 191
+ { 0, 64, 0 }, // 192
+ { 0, 64, 16 }, // 193
+ { 0, 64, 32 }, // 194
+ { 0, 64, 48 }, // 195
+ { 0, 64, 64 }, // 196
+ { 0, 48, 64 }, // 197
+ { 0, 32, 64 }, // 198
+ { 0, 16, 64 }, // 199
+ { 32, 32, 64 }, // 200
+ { 40, 32, 64 }, // 201
+ { 48, 32, 64 }, // 202
+ { 56, 32, 64 }, // 203
+ { 64, 32, 64 }, // 204
+ { 64, 32, 56 }, // 205
+ { 64, 32, 48 }, // 206
+ { 64, 32, 40 }, // 207
+ { 64, 32, 32 }, // 208
+ { 64, 40, 32 }, // 209
+ { 64, 48, 32 }, // 210
+ { 64, 56, 32 }, // 211
+ { 64, 64, 32 }, // 212
+ { 56, 64, 32 }, // 213
+ { 48, 64, 32 }, // 214
+ { 40, 64, 32 }, // 215
+ { 32, 64, 32 }, // 216
+ { 32, 64, 40 }, // 217
+ { 32, 64, 48 }, // 218
+ { 32, 64, 56 }, // 219
+ { 32, 64, 64 }, // 220
+ { 32, 56, 64 }, // 221
+ { 32, 48, 64 }, // 222
+ { 32, 40, 64 }, // 223
+ { 44, 44, 64 }, // 224
+ { 48, 44, 64 }, // 225
+ { 52, 44, 64 }, // 226
+ { 60, 44, 64 }, // 227
+ { 64, 44, 64 }, // 228
+ { 64, 44, 60 }, // 229
+ { 64, 44, 52 }, // 230
+ { 64, 44, 48 }, // 231
+ { 64, 44, 44 }, // 232
+ { 64, 48, 44 }, // 233
+ { 64, 52, 44 }, // 234
+ { 64, 60, 44 }, // 235
+ { 64, 64, 44 }, // 236
+ { 60, 64, 44 }, // 237
+ { 52, 64, 44 }, // 238
+ { 48, 64, 44 }, // 239
+ { 44, 64, 44 }, // 240
+ { 44, 64, 48 }, // 241
+ { 44, 64, 52 }, // 242
+ { 44, 64, 60 }, // 243
+ { 44, 64, 64 }, // 244
+ { 44, 60, 64 }, // 245
+ { 44, 52, 64 }, // 246
+ { 44, 48, 64 }, // 247
+ { 0, 0, 0 }, // 248
+ { 0, 0, 0 }, // 249
+ { 0, 0, 0 }, // 250
+ { 0, 0, 0 }, // 251
+ { 0, 0, 0 }, // 252
+ { 0, 0, 0 }, // 253
+ { 0, 0, 0 }, // 254
+ { 112, 97, 108 }}; // 255
+*/
diff --git a/tools/ioemu/gui/sdlkeys.h b/tools/ioemu/gui/sdlkeys.h
new file mode 100644
index 0000000000..fd1197a5f1
--- /dev/null
+++ b/tools/ioemu/gui/sdlkeys.h
@@ -0,0 +1,257 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: sdlkeys.h,v 1.2 2002/10/24 21:06:32 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// This file is simply a list of SDL key symbols taken from <SDL/SDL_keysym.h>.
+// The order in this file is not important. In sdl.cc, DEF_SDL_KEY() is
+// defined as a macro and then it includes this file to fill in all the data in
+// its key mapping table.
+//
+// The symbols, such as SDLK_RETURN, are used for two purposes. They
+// are converted into a string (by the # operator in processor), which is
+// compared to the host key name in the keymap file. Also, the value of
+// the symbol is inserted into the key mapping table. Then the value is
+// compared with the keysym field of each key up/down event as it arrives.
+//
+// If you get undefined symbol errors in this file, it must mean that
+// your SDL library version doesn't define those same SDLK_* symbols in
+// <SDL/SDL_keysym.h>. You can't fix it with #ifdef SDLK_SYM because
+// they are enums, so you'll just have to comment out the offending line.
+// The list was generated using symbols from SDL 1.2.3.
+
+DEF_SDL_KEY( SDLK_UNKNOWN )
+DEF_SDL_KEY( SDLK_FIRST )
+DEF_SDL_KEY( SDLK_BACKSPACE )
+DEF_SDL_KEY( SDLK_TAB )
+DEF_SDL_KEY( SDLK_CLEAR )
+DEF_SDL_KEY( SDLK_RETURN )
+DEF_SDL_KEY( SDLK_PAUSE )
+DEF_SDL_KEY( SDLK_ESCAPE )
+DEF_SDL_KEY( SDLK_SPACE )
+DEF_SDL_KEY( SDLK_EXCLAIM )
+DEF_SDL_KEY( SDLK_QUOTEDBL )
+DEF_SDL_KEY( SDLK_HASH )
+DEF_SDL_KEY( SDLK_DOLLAR )
+DEF_SDL_KEY( SDLK_AMPERSAND )
+DEF_SDL_KEY( SDLK_QUOTE )
+DEF_SDL_KEY( SDLK_LEFTPAREN )
+DEF_SDL_KEY( SDLK_RIGHTPAREN )
+DEF_SDL_KEY( SDLK_ASTERISK )
+DEF_SDL_KEY( SDLK_PLUS )
+DEF_SDL_KEY( SDLK_COMMA )
+DEF_SDL_KEY( SDLK_MINUS )
+DEF_SDL_KEY( SDLK_PERIOD )
+DEF_SDL_KEY( SDLK_SLASH )
+DEF_SDL_KEY( SDLK_0 )
+DEF_SDL_KEY( SDLK_1 )
+DEF_SDL_KEY( SDLK_2 )
+DEF_SDL_KEY( SDLK_3 )
+DEF_SDL_KEY( SDLK_4 )
+DEF_SDL_KEY( SDLK_5 )
+DEF_SDL_KEY( SDLK_6 )
+DEF_SDL_KEY( SDLK_7 )
+DEF_SDL_KEY( SDLK_8 )
+DEF_SDL_KEY( SDLK_9 )
+DEF_SDL_KEY( SDLK_COLON )
+DEF_SDL_KEY( SDLK_SEMICOLON )
+DEF_SDL_KEY( SDLK_LESS )
+DEF_SDL_KEY( SDLK_EQUALS )
+DEF_SDL_KEY( SDLK_GREATER )
+DEF_SDL_KEY( SDLK_QUESTION )
+DEF_SDL_KEY( SDLK_AT )
+DEF_SDL_KEY( /* )
+DEF_SDL_KEY( Skip uppercase letters )
+DEF_SDL_KEY( */ )
+DEF_SDL_KEY( SDLK_LEFTBRACKET )
+DEF_SDL_KEY( SDLK_BACKSLASH )
+DEF_SDL_KEY( SDLK_RIGHTBRACKET )
+DEF_SDL_KEY( SDLK_CARET )
+DEF_SDL_KEY( SDLK_UNDERSCORE )
+DEF_SDL_KEY( SDLK_BACKQUOTE )
+DEF_SDL_KEY( SDLK_a )
+DEF_SDL_KEY( SDLK_b )
+DEF_SDL_KEY( SDLK_c )
+DEF_SDL_KEY( SDLK_d )
+DEF_SDL_KEY( SDLK_e )
+DEF_SDL_KEY( SDLK_f )
+DEF_SDL_KEY( SDLK_g )
+DEF_SDL_KEY( SDLK_h )
+DEF_SDL_KEY( SDLK_i )
+DEF_SDL_KEY( SDLK_j )
+DEF_SDL_KEY( SDLK_k )
+DEF_SDL_KEY( SDLK_l )
+DEF_SDL_KEY( SDLK_m )
+DEF_SDL_KEY( SDLK_n )
+DEF_SDL_KEY( SDLK_o )
+DEF_SDL_KEY( SDLK_p )
+DEF_SDL_KEY( SDLK_q )
+DEF_SDL_KEY( SDLK_r )
+DEF_SDL_KEY( SDLK_s )
+DEF_SDL_KEY( SDLK_t )
+DEF_SDL_KEY( SDLK_u )
+DEF_SDL_KEY( SDLK_v )
+DEF_SDL_KEY( SDLK_w )
+DEF_SDL_KEY( SDLK_x )
+DEF_SDL_KEY( SDLK_y )
+DEF_SDL_KEY( SDLK_z )
+DEF_SDL_KEY( SDLK_DELETE )
+DEF_SDL_KEY( SDLK_WORLD_0 )
+DEF_SDL_KEY( SDLK_WORLD_1 )
+DEF_SDL_KEY( SDLK_WORLD_2 )
+DEF_SDL_KEY( SDLK_WORLD_3 )
+DEF_SDL_KEY( SDLK_WORLD_4 )
+DEF_SDL_KEY( SDLK_WORLD_5 )
+DEF_SDL_KEY( SDLK_WORLD_6 )
+DEF_SDL_KEY( SDLK_WORLD_7 )
+DEF_SDL_KEY( SDLK_WORLD_8 )
+DEF_SDL_KEY( SDLK_WORLD_9 )
+DEF_SDL_KEY( SDLK_WORLD_10 )
+DEF_SDL_KEY( SDLK_WORLD_11 )
+DEF_SDL_KEY( SDLK_WORLD_12 )
+DEF_SDL_KEY( SDLK_WORLD_13 )
+DEF_SDL_KEY( SDLK_WORLD_14 )
+DEF_SDL_KEY( SDLK_WORLD_15 )
+DEF_SDL_KEY( SDLK_WORLD_16 )
+DEF_SDL_KEY( SDLK_WORLD_17 )
+DEF_SDL_KEY( SDLK_WORLD_18 )
+DEF_SDL_KEY( SDLK_WORLD_19 )
+DEF_SDL_KEY( SDLK_WORLD_20 )
+DEF_SDL_KEY( SDLK_WORLD_21 )
+DEF_SDL_KEY( SDLK_WORLD_22 )
+DEF_SDL_KEY( SDLK_WORLD_23 )
+DEF_SDL_KEY( SDLK_WORLD_24 )
+DEF_SDL_KEY( SDLK_WORLD_25 )
+DEF_SDL_KEY( SDLK_WORLD_26 )
+DEF_SDL_KEY( SDLK_WORLD_27 )
+DEF_SDL_KEY( SDLK_WORLD_28 )
+DEF_SDL_KEY( SDLK_WORLD_29 )
+DEF_SDL_KEY( SDLK_WORLD_30 )
+DEF_SDL_KEY( SDLK_WORLD_31 )
+DEF_SDL_KEY( SDLK_WORLD_32 )
+DEF_SDL_KEY( SDLK_WORLD_33 )
+DEF_SDL_KEY( SDLK_WORLD_34 )
+DEF_SDL_KEY( SDLK_WORLD_35 )
+DEF_SDL_KEY( SDLK_WORLD_36 )
+DEF_SDL_KEY( SDLK_WORLD_37 )
+DEF_SDL_KEY( SDLK_WORLD_38 )
+DEF_SDL_KEY( SDLK_WORLD_39 )
+DEF_SDL_KEY( SDLK_WORLD_40 )
+DEF_SDL_KEY( SDLK_WORLD_41 )
+DEF_SDL_KEY( SDLK_WORLD_42 )
+DEF_SDL_KEY( SDLK_WORLD_43 )
+DEF_SDL_KEY( SDLK_WORLD_44 )
+DEF_SDL_KEY( SDLK_WORLD_45 )
+DEF_SDL_KEY( SDLK_WORLD_46 )
+DEF_SDL_KEY( SDLK_WORLD_47 )
+DEF_SDL_KEY( SDLK_WORLD_48 )
+DEF_SDL_KEY( SDLK_WORLD_49 )
+DEF_SDL_KEY( SDLK_WORLD_50 )
+DEF_SDL_KEY( SDLK_WORLD_51 )
+DEF_SDL_KEY( SDLK_WORLD_52 )
+DEF_SDL_KEY( SDLK_WORLD_53 )
+DEF_SDL_KEY( SDLK_WORLD_54 )
+DEF_SDL_KEY( SDLK_WORLD_55 )
+DEF_SDL_KEY( SDLK_WORLD_56 )
+DEF_SDL_KEY( SDLK_WORLD_57 )
+DEF_SDL_KEY( SDLK_WORLD_58 )
+DEF_SDL_KEY( SDLK_WORLD_59 )
+DEF_SDL_KEY( SDLK_WORLD_60 )
+DEF_SDL_KEY( SDLK_WORLD_61 )
+DEF_SDL_KEY( SDLK_WORLD_62 )
+DEF_SDL_KEY( SDLK_WORLD_63 )
+DEF_SDL_KEY( SDLK_WORLD_64 )
+DEF_SDL_KEY( SDLK_WORLD_65 )
+DEF_SDL_KEY( SDLK_WORLD_66 )
+DEF_SDL_KEY( SDLK_WORLD_67 )
+DEF_SDL_KEY( SDLK_WORLD_68 )
+DEF_SDL_KEY( SDLK_WORLD_69 )
+DEF_SDL_KEY( SDLK_WORLD_70 )
+DEF_SDL_KEY( SDLK_WORLD_71 )
+DEF_SDL_KEY( SDLK_WORLD_72 )
+DEF_SDL_KEY( SDLK_WORLD_73 )
+DEF_SDL_KEY( SDLK_WORLD_74 )
+DEF_SDL_KEY( SDLK_WORLD_75 )
+DEF_SDL_KEY( SDLK_WORLD_76 )
+DEF_SDL_KEY( SDLK_WORLD_77 )
+DEF_SDL_KEY( SDLK_WORLD_78 )
+DEF_SDL_KEY( SDLK_WORLD_79 )
+DEF_SDL_KEY( SDLK_WORLD_80 )
+DEF_SDL_KEY( SDLK_WORLD_81 )
+DEF_SDL_KEY( SDLK_WORLD_82 )
+DEF_SDL_KEY( SDLK_WORLD_83 )
+DEF_SDL_KEY( SDLK_WORLD_84 )
+DEF_SDL_KEY( SDLK_WORLD_85 )
+DEF_SDL_KEY( SDLK_WORLD_86 )
+DEF_SDL_KEY( SDLK_WORLD_87 )
+DEF_SDL_KEY( SDLK_WORLD_88 )
+DEF_SDL_KEY( SDLK_WORLD_89 )
+DEF_SDL_KEY( SDLK_WORLD_90 )
+DEF_SDL_KEY( SDLK_WORLD_91 )
+DEF_SDL_KEY( SDLK_WORLD_92 )
+DEF_SDL_KEY( SDLK_WORLD_93 )
+DEF_SDL_KEY( SDLK_WORLD_94 )
+DEF_SDL_KEY( SDLK_WORLD_95 )
+DEF_SDL_KEY( SDLK_KP0 )
+DEF_SDL_KEY( SDLK_KP1 )
+DEF_SDL_KEY( SDLK_KP2 )
+DEF_SDL_KEY( SDLK_KP3 )
+DEF_SDL_KEY( SDLK_KP4 )
+DEF_SDL_KEY( SDLK_KP5 )
+DEF_SDL_KEY( SDLK_KP6 )
+DEF_SDL_KEY( SDLK_KP7 )
+DEF_SDL_KEY( SDLK_KP8 )
+DEF_SDL_KEY( SDLK_KP9 )
+DEF_SDL_KEY( SDLK_KP_PERIOD )
+DEF_SDL_KEY( SDLK_KP_DIVIDE )
+DEF_SDL_KEY( SDLK_KP_MULTIPLY )
+DEF_SDL_KEY( SDLK_KP_MINUS )
+DEF_SDL_KEY( SDLK_KP_PLUS )
+DEF_SDL_KEY( SDLK_KP_ENTER )
+DEF_SDL_KEY( SDLK_KP_EQUALS )
+DEF_SDL_KEY( SDLK_UP )
+DEF_SDL_KEY( SDLK_DOWN )
+DEF_SDL_KEY( SDLK_RIGHT )
+DEF_SDL_KEY( SDLK_LEFT )
+DEF_SDL_KEY( SDLK_INSERT )
+DEF_SDL_KEY( SDLK_HOME )
+DEF_SDL_KEY( SDLK_END )
+DEF_SDL_KEY( SDLK_PAGEUP )
+DEF_SDL_KEY( SDLK_PAGEDOWN )
+DEF_SDL_KEY( SDLK_F1 )
+DEF_SDL_KEY( SDLK_F2 )
+DEF_SDL_KEY( SDLK_F3 )
+DEF_SDL_KEY( SDLK_F4 )
+DEF_SDL_KEY( SDLK_F5 )
+DEF_SDL_KEY( SDLK_F6 )
+DEF_SDL_KEY( SDLK_F7 )
+DEF_SDL_KEY( SDLK_F8 )
+DEF_SDL_KEY( SDLK_F9 )
+DEF_SDL_KEY( SDLK_F10 )
+DEF_SDL_KEY( SDLK_F11 )
+DEF_SDL_KEY( SDLK_F12 )
+DEF_SDL_KEY( SDLK_F13 )
+DEF_SDL_KEY( SDLK_F14 )
+DEF_SDL_KEY( SDLK_F15 )
+DEF_SDL_KEY( SDLK_NUMLOCK )
+DEF_SDL_KEY( SDLK_CAPSLOCK )
+DEF_SDL_KEY( SDLK_SCROLLOCK )
+DEF_SDL_KEY( SDLK_RSHIFT )
+DEF_SDL_KEY( SDLK_LSHIFT )
+DEF_SDL_KEY( SDLK_RCTRL )
+DEF_SDL_KEY( SDLK_LCTRL )
+DEF_SDL_KEY( SDLK_RALT )
+DEF_SDL_KEY( SDLK_LALT )
+DEF_SDL_KEY( SDLK_RMETA )
+DEF_SDL_KEY( SDLK_LMETA )
+DEF_SDL_KEY( SDLK_LSUPER )
+DEF_SDL_KEY( SDLK_RSUPER )
+DEF_SDL_KEY( SDLK_MODE )
+DEF_SDL_KEY( SDLK_COMPOSE )
+DEF_SDL_KEY( SDLK_HELP )
+DEF_SDL_KEY( SDLK_PRINT )
+DEF_SDL_KEY( SDLK_SYSREQ )
+DEF_SDL_KEY( SDLK_BREAK )
+DEF_SDL_KEY( SDLK_MENU )
+DEF_SDL_KEY( SDLK_POWER )
+DEF_SDL_KEY( SDLK_EURO )
+DEF_SDL_KEY( SDLK_UNDO )
diff --git a/tools/ioemu/gui/siminterface.cc b/tools/ioemu/gui/siminterface.cc
new file mode 100644
index 0000000000..0284e6b1d7
--- /dev/null
+++ b/tools/ioemu/gui/siminterface.cc
@@ -0,0 +1,1411 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: siminterface.cc,v 1.105 2004/01/05 22:18:01 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// See siminterface.h for description of the siminterface concept.
+// Basically, the siminterface is visible from both the simulator and
+// the configuration user interface, and allows them to talk to each other.
+
+#include "bochs.h"
+
+bx_simulator_interface_c *SIM = NULL;
+logfunctions *siminterface_log = NULL;
+#define LOG_THIS siminterface_log->
+
+// bx_simulator_interface just defines the interface that the Bochs simulator
+// and the gui will use to talk to each other. None of the methods of
+// bx_simulator_interface are implemented; they are all virtual. The
+// bx_real_sim_c class is a child of bx_simulator_interface_c, and it
+// implements all the methods. The idea is that a gui needs to know only
+// definition of bx_simulator_interface to talk to Bochs. The gui should
+// not need to include bochs.h.
+//
+// I made this separation to ensure that all guis use the siminterface to do
+// access bochs internals, instead of accessing things like
+// bx_keyboard.s.internal_buffer[4] (or whatever) directly. -Bryce
+//
+
+class bx_real_sim_c : public bx_simulator_interface_c {
+ bxevent_handler bxevent_callback;
+ void *bxevent_callback_data;
+ const char *registered_ci_name;
+ config_interface_callback_t ci_callback;
+ void *ci_callback_data;
+ int init_done;
+ bx_param_c **param_registry;
+ int registry_alloc_size;
+ int enabled;
+ // save context to jump to if we must quit unexpectedly
+ jmp_buf *quit_context;
+ int exit_code;
+public:
+ bx_real_sim_c ();
+ virtual ~bx_real_sim_c ();
+ virtual void set_quit_context (jmp_buf *context) { quit_context = context; }
+ virtual int get_init_done () { return init_done; }
+ virtual int set_init_done (int n) { init_done = n; return 0;}
+ virtual void get_param_id_range (int *min, int *max) {
+ *min = BXP_NULL;
+ *max = BXP_THIS_IS_THE_LAST-1;
+ }
+ virtual int register_param (bx_id id, bx_param_c *it);
+ virtual void reset_all_param ();
+ virtual bx_param_c *get_param (bx_id id);
+ virtual bx_param_num_c *get_param_num (bx_id id);
+ virtual bx_param_string_c *get_param_string (bx_id id);
+ virtual bx_param_bool_c *get_param_bool (bx_id id);
+ virtual bx_param_enum_c *get_param_enum (bx_id id);
+ virtual int get_n_log_modules ();
+ virtual char *get_prefix (int mod);
+ virtual int get_log_action (int mod, int level);
+ virtual void set_log_action (int mod, int level, int action);
+ virtual char *get_action_name (int action);
+ virtual int get_default_log_action (int level) {
+ return logfunctions::get_default_action (level);
+ }
+ virtual void set_default_log_action (int level, int action) {
+ logfunctions::set_default_action (level, action);
+ }
+ virtual const char *get_log_level_name (int level);
+ virtual int get_max_log_level ();
+ virtual void quit_sim (int code);
+ virtual int get_exit_code () { return exit_code; }
+ virtual int get_default_rc (char *path, int len);
+ virtual int read_rc (char *path);
+ virtual int write_rc (char *path, int overwrite);
+ virtual int get_log_file (char *path, int len);
+ virtual int set_log_file (char *path);
+ virtual int get_log_prefix (char *prefix, int len);
+ virtual int set_log_prefix (char *prefix);
+ virtual int get_debugger_log_file (char *path, int len);
+ virtual int set_debugger_log_file (char *path);
+ virtual int get_floppy_options (int drive, bx_floppy_options *out);
+ virtual int get_cdrom_options (int drive, bx_atadevice_options *out, int *device = NULL);
+ virtual char *get_floppy_type_name (int type);
+ virtual void set_notify_callback (bxevent_handler func, void *arg);
+ virtual void get_notify_callback (bxevent_handler *func, void **arg);
+ virtual BxEvent* sim_to_ci_event (BxEvent *event);
+ virtual int log_msg (const char *prefix, int level, const char *msg);
+ virtual int ask_param (bx_id which);
+ // ask the user for a pathname
+ virtual int ask_filename (char *filename, int maxlen, char *prompt, char *the_default, int flags);
+ // called at a regular interval, currently by the keyboard handler.
+ virtual void periodic ();
+ virtual int create_disk_image (const char *filename, int sectors, bx_bool overwrite);
+ virtual void refresh_ci ();
+ virtual void refresh_vga () {
+ // maybe need to check if something has been initialized yet?
+ DEV_vga_refresh();
+ }
+ virtual void handle_events () {
+ // maybe need to check if something has been initialized yet?
+ bx_gui->handle_events ();
+ }
+ // find first hard drive or cdrom
+ bx_param_c *get_first_atadevice (Bit32u search_type);
+ bx_param_c *get_first_cdrom () {
+ return get_first_atadevice (BX_ATA_DEVICE_CDROM);
+ }
+ bx_param_c *get_first_hd () {
+ return get_first_atadevice (BX_ATA_DEVICE_DISK);
+ }
+#if BX_DEBUGGER
+ virtual void debug_break ();
+ virtual void debug_interpret_cmd (char *cmd);
+ virtual char *debug_get_next_command ();
+ virtual void debug_puts (const char *cmd);
+#endif
+ virtual void register_configuration_interface (
+ const char* name,
+ config_interface_callback_t callback,
+ void *userdata);
+ virtual int configuration_interface(const char* name, ci_command_t command);
+ virtual int begin_simulation (int argc, char *argv[]);
+ virtual void set_sim_thread_func (is_sim_thread_func_t func) {}
+ virtual bool is_sim_thread ();
+ bool wxsel;
+ virtual bool is_wx_selected () { return wxsel; }
+ // provide interface to bx_gui->set_display_mode() method for config
+ // interfaces to use.
+ virtual void set_display_mode (disp_mode_t newmode) {
+ if (bx_gui != NULL)
+ bx_gui->set_display_mode (newmode);
+ }
+ virtual bool test_for_text_console ();
+};
+
+bx_param_c *
+bx_real_sim_c::get_param (bx_id id)
+{
+ BX_ASSERT (id >= BXP_NULL && id < BXP_THIS_IS_THE_LAST);
+ int index = (int)id - BXP_NULL;
+ bx_param_c *retval = param_registry[index];
+ if (!retval)
+ BX_INFO (("get_param can't find id %u", id));
+ return retval;
+}
+
+bx_param_num_c *
+bx_real_sim_c::get_param_num (bx_id id) {
+ bx_param_c *generic = get_param(id);
+ if (generic==NULL) {
+ BX_PANIC (("get_param_num(%u) could not find a parameter", id));
+ return NULL;
+ }
+ int type = generic->get_type ();
+ if (type == BXT_PARAM_NUM || type == BXT_PARAM_BOOL || type == BXT_PARAM_ENUM)
+ return (bx_param_num_c *)generic;
+ BX_PANIC (("get_param_num %u could not find an integer parameter with that id", id));
+ return NULL;
+}
+
+bx_param_string_c *
+bx_real_sim_c::get_param_string (bx_id id) {
+ bx_param_c *generic = get_param(id);
+ if (generic==NULL) {
+ BX_PANIC (("get_param_string(%u) could not find a parameter", id));
+ return NULL;
+ }
+ if (generic->get_type () == BXT_PARAM_STRING)
+ return (bx_param_string_c *)generic;
+ BX_PANIC (("get_param_string %u could not find an integer parameter with that id", id));
+ return NULL;
+}
+
+bx_param_bool_c *
+bx_real_sim_c::get_param_bool (bx_id id) {
+ bx_param_c *generic = get_param(id);
+ if (generic==NULL) {
+ BX_PANIC (("get_param_bool(%u) could not find a parameter", id));
+ return NULL;
+ }
+ if (generic->get_type () == BXT_PARAM_BOOL)
+ return (bx_param_bool_c *)generic;
+ BX_PANIC (("get_param_bool %u could not find a bool parameter with that id", id));
+ return NULL;
+}
+
+bx_param_enum_c *
+bx_real_sim_c::get_param_enum (bx_id id) {
+ bx_param_c *generic = get_param(id);
+ if (generic==NULL) {
+ BX_PANIC (("get_param_enum(%u) could not find a parameter", id));
+ return NULL;
+ }
+ if (generic->get_type () == BXT_PARAM_ENUM)
+ return (bx_param_enum_c *)generic;
+ BX_PANIC (("get_param_enum %u could not find a enum parameter with that id", id));
+ return NULL;
+}
+
+void bx_init_siminterface ()
+{
+ siminterface_log = new logfunctions ();
+ siminterface_log->put ("CTRL");
+ siminterface_log->settype(CTRLLOG);
+ if (SIM == NULL)
+ SIM = new bx_real_sim_c();
+}
+
+bx_simulator_interface_c::bx_simulator_interface_c ()
+{
+}
+
+bx_real_sim_c::bx_real_sim_c ()
+{
+ bxevent_callback = NULL;
+ bxevent_callback_data = NULL;
+ ci_callback = NULL;
+ ci_callback_data = NULL;
+ is_sim_thread_func = NULL;
+ wxsel = false;
+
+ enabled = 1;
+ int i;
+ init_done = 0;
+ registry_alloc_size = BXP_THIS_IS_THE_LAST - BXP_NULL;
+ param_registry = new bx_param_c* [registry_alloc_size];
+ for (i=0; i<registry_alloc_size; i++)
+ param_registry[i] = NULL;
+ quit_context = NULL;
+ exit_code = 0;
+}
+
+// called by constructor of bx_param_c, so that every parameter that is
+// initialized gets registered. This builds a list of all parameters
+// which can be used to look them up by number (get_param).
+bx_real_sim_c::~bx_real_sim_c ()
+{
+ if ( param_registry != NULL )
+ {
+ delete [] param_registry;
+ param_registry = NULL;
+ }
+}
+
+int
+bx_real_sim_c::register_param (bx_id id, bx_param_c *it)
+{
+ if (id == BXP_NULL) return 0;
+ BX_ASSERT (id >= BXP_NULL && id < BXP_THIS_IS_THE_LAST);
+ int index = (int)id - BXP_NULL;
+ if (this->param_registry[index] != NULL) {
+ BX_INFO (("register_param is overwriting parameter id %d", id));
+ }
+ this->param_registry[index] = it;
+ return 0;
+}
+
+void
+bx_real_sim_c::reset_all_param ()
+{
+ bx_reset_options ();
+}
+
+int
+bx_real_sim_c::get_n_log_modules ()
+{
+ return io->get_n_logfns ();
+}
+
+char *
+bx_real_sim_c::get_prefix (int mod)
+{
+ logfunc_t *logfn = io->get_logfn (mod);
+ return logfn->getprefix ();
+}
+
+int
+bx_real_sim_c::get_log_action (int mod, int level)
+{
+ logfunc_t *logfn = io->get_logfn (mod);
+ return logfn->getonoff (level);
+}
+
+void
+bx_real_sim_c::set_log_action (int mod, int level, int action)
+{
+ // normal
+ if (mod >= 0) {
+ logfunc_t *logfn = io->get_logfn (mod);
+ logfn->setonoff (level, action);
+ return;
+ }
+ // if called with mod<0 loop over all
+ int nmod = get_n_log_modules ();
+ for (mod=0; mod<nmod; mod++)
+ set_log_action (mod, level, action);
+}
+
+char *
+bx_real_sim_c::get_action_name (int action)
+{
+ return io->getaction (action);
+}
+
+const char *
+bx_real_sim_c::get_log_level_name (int level)
+{
+ return io->getlevel (level);
+}
+
+int
+bx_real_sim_c::get_max_log_level ()
+{
+ return N_LOGLEV;
+}
+
+void
+bx_real_sim_c::quit_sim (int code) {
+ BX_INFO (("quit_sim called with exit code %d", code));
+ exit_code = code;
+ // use longjmp to quit cleanly, no matter where in the stack we are.
+ //fprintf (stderr, "using longjmp() to jump directly to the quit context!\n");
+ if (quit_context != NULL) {
+ longjmp (*quit_context, 1);
+ BX_PANIC (("in bx_real_sim_c::quit_sim, longjmp should never return"));
+ }
+ if (SIM->is_wx_selected ()) {
+ // in wxWindows, the whole simulator is running in a separate thread.
+ // our only job is to end the thread as soon as possible, NOT to shut
+ // down the whole application with an exit.
+ BX_CPU(0)->async_event = 1;
+ BX_CPU(0)->kill_bochs_request = 1;
+ // the cpu loop will exit very soon after this condition is set.
+ } else {
+ // just a single thread. Use exit() to stop the application.
+ if (!code)
+ BX_PANIC (("Quit simulation command"));
+ ::exit (exit_code);
+ }
+}
+
+int
+bx_real_sim_c::get_default_rc (char *path, int len)
+{
+ char *rc = bx_find_bochsrc ();
+ if (rc == NULL) return -1;
+ strncpy (path, rc, len);
+ path[len-1] = 0;
+ return 0;
+}
+
+int
+bx_real_sim_c::read_rc (char *rc)
+{
+ return bx_read_configuration (rc);
+}
+
+// return values:
+// 0: written ok
+// -1: failed
+// -2: already exists, and overwrite was off
+int
+bx_real_sim_c::write_rc (char *rc, int overwrite)
+{
+ return bx_write_configuration (rc, overwrite);
+}
+
+int
+bx_real_sim_c::get_log_file (char *path, int len)
+{
+ strncpy (path, bx_options.log.Ofilename->getptr (), len);
+ return 0;
+}
+
+int
+bx_real_sim_c::set_log_file (char *path)
+{
+ bx_options.log.Ofilename->set (path);
+ return 0;
+}
+
+int
+bx_real_sim_c::get_log_prefix (char *prefix, int len)
+{
+ strncpy (prefix, bx_options.log.Oprefix->getptr (), len);
+ return 0;
+}
+
+int
+bx_real_sim_c::set_log_prefix (char *prefix)
+{
+ bx_options.log.Oprefix->set (prefix);
+ return 0;
+}
+
+int
+bx_real_sim_c::get_debugger_log_file (char *path, int len)
+{
+ strncpy (path, bx_options.log.Odebugger_filename->getptr (), len);
+ return 0;
+}
+
+int
+bx_real_sim_c::set_debugger_log_file (char *path)
+{
+ bx_options.log.Odebugger_filename->set (path);
+ return 0;
+}
+
+int
+bx_real_sim_c::get_floppy_options (int drive, bx_floppy_options *out)
+{
+ *out = (drive==0)? bx_options.floppya : bx_options.floppyb;
+ return 0;
+}
+
+int
+bx_real_sim_c::get_cdrom_options (int level, bx_atadevice_options *out, int *where)
+{
+ for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ for (Bit8u device=0; device<2; device++) {
+ if (bx_options.atadevice[channel][device].Otype->get() == BX_ATA_DEVICE_CDROM) {
+ if (level==0) {
+ *out = bx_options.atadevice[channel][device];
+ if (where != NULL) *where=(channel*2)+device;
+ return 1;
+ }
+ else level--;
+ }
+ }
+ }
+ return 0;
+}
+
+char *bochs_start_names[] = { "quick", "load", "edit", "run" };
+int n_bochs_start_names = 3;
+
+char *floppy_type_names[] = { "none", "1.2M", "1.44M", "2.88M", "720K", "360K", "160K", "180K", "320K", NULL };
+int floppy_type_n_sectors[] = { -1, 80*2*15, 80*2*18, 80*2*36, 80*2*9, 40*2*9, 40*1*8, 40*1*9, 40*2*8 };
+int n_floppy_type_names = 9;
+
+char *floppy_status_names[] = { "ejected", "inserted", NULL };
+int n_floppy_status_names = 2;
+char *floppy_bootdisk_names[] = { "floppy", "disk","cdrom", NULL };
+int n_floppy_bootdisk_names = 3;
+char *loader_os_names[] = { "none", "linux", "nullkernel", NULL };
+int n_loader_os_names = 3;
+char *keyboard_type_names[] = { "xt", "at", "mf", NULL };
+int n_keyboard_type_names = 3;
+
+char *atadevice_type_names[] = { "disk", "cdrom", NULL };
+int n_atadevice_type_names = 2;
+//char *atadevice_mode_names[] = { "flat", "concat", "external", "dll", "sparse", "vmware3", "split", "undoable", "growing", "volatile", "z-undoable", "z-volatile", NULL };
+char *atadevice_mode_names[] = { "flat", "concat", "external", "dll", "sparse", "vmware3", "undoable", "growing", "volatile", NULL };
+int n_atadevice_mode_names = 9;
+char *atadevice_status_names[] = { "ejected", "inserted", NULL };
+int n_atadevice_status_names = 2;
+char *atadevice_biosdetect_names[] = { "none", "auto", "cmos", NULL };
+int n_atadevice_biosdetect_names = 3;
+char *atadevice_translation_names[] = { "none", "lba", "large", "rechs", "auto", NULL };
+int n_atadevice_translation_names = 5;
+char *clock_sync_names[] = { "none", "realtime", "slowdown", "both", NULL };
+int clock_sync_n_names=4;
+
+
+
+char *
+bx_real_sim_c::get_floppy_type_name (int type)
+{
+ BX_ASSERT (type >= BX_FLOPPY_NONE && type <= BX_FLOPPY_LAST);
+ type -= BX_FLOPPY_NONE;
+ return floppy_type_names[type];
+}
+
+void
+bx_real_sim_c::set_notify_callback (bxevent_handler func, void *arg)
+{
+ bxevent_callback = func;
+ bxevent_callback_data = arg;
+}
+
+void bx_real_sim_c::get_notify_callback (
+ bxevent_handler *func,
+ void **arg)
+{
+ *func = bxevent_callback;
+ *arg = bxevent_callback_data;
+}
+
+BxEvent *
+bx_real_sim_c::sim_to_ci_event (BxEvent *event)
+{
+ if (bxevent_callback == NULL) {
+ BX_ERROR (("notify called, but no bxevent_callback function is registered"));
+ return NULL;
+ } else {
+ return (*bxevent_callback)(bxevent_callback_data, event);
+ }
+}
+
+// returns 0 for continue, 1 for alwayscontinue, 2 for die.
+int
+bx_real_sim_c::log_msg (const char *prefix, int level, const char *msg)
+{
+ BxEvent be;
+ be.type = BX_SYNC_EVT_LOG_ASK;
+ be.u.logmsg.prefix = prefix;
+ be.u.logmsg.level = level;
+ be.u.logmsg.msg = msg;
+ // default return value in case something goes wrong.
+ be.retcode = BX_LOG_ASK_CHOICE_DIE;
+ //fprintf (stderr, "calling notify.\n");
+ sim_to_ci_event (&be);
+ return be.retcode;
+}
+
+// Called by simulator whenever it needs the user to choose a new value
+// for a registered parameter. Create a synchronous ASK_PARAM event,
+// send it to the CI, and wait for the response. The CI will call the
+// set() method on the parameter if the user changes the value.
+int
+bx_real_sim_c::ask_param (bx_id param)
+{
+ bx_param_c *paramptr = SIM->get_param(param);
+ BX_ASSERT (paramptr != NULL);
+ // create appropriate event
+ BxEvent event;
+ event.type = BX_SYNC_EVT_ASK_PARAM;
+ event.u.param.param = paramptr;
+ sim_to_ci_event (&event);
+ return event.retcode;
+}
+
+int
+bx_real_sim_c::ask_filename (char *filename, int maxlen, char *prompt, char *the_default, int flags)
+{
+ // implement using ASK_PARAM on a newly created param. I can't use
+ // ask_param because I don't intend to register this param.
+ BxEvent event;
+ bx_param_string_c param (BXP_NULL, prompt, "filename", the_default, maxlen);
+ flags |= param.IS_FILENAME;
+ param.get_options()->set (flags);
+ event.type = BX_SYNC_EVT_ASK_PARAM;
+ event.u.param.param = &param;
+ sim_to_ci_event (&event);
+ if (event.retcode >= 0)
+ memcpy (filename, param.getptr(), maxlen);
+ return event.retcode;
+}
+
+void
+bx_real_sim_c::periodic ()
+{
+ // give the GUI a chance to do periodic things on the bochs thread. in
+ // particular, notice if the thread has been asked to die.
+ BxEvent tick;
+ tick.type = BX_SYNC_EVT_TICK;
+ sim_to_ci_event (&tick);
+ if (tick.retcode < 0) {
+ BX_INFO (("Bochs thread has been asked to quit."));
+ bx_atexit ();
+ quit_sim (0);
+ }
+ static int refresh_counter = 0;
+ if (++refresh_counter == 50) {
+ // only ask the CI to refresh every 50 times periodic() is called.
+ // This should obviously be configurable because system speeds and
+ // user preferences vary.
+ refresh_ci ();
+ refresh_counter = 0;
+ }
+#if 0
+ // watch for memory leaks. Allocate a small block of memory, print the
+ // pointer that is returned, then free.
+ BxEvent *memcheck = new BxEvent ();
+ BX_INFO(("memory allocation at %p", memcheck));
+ delete memcheck;
+#endif
+}
+
+// create a disk image file called filename, size=512 bytes * sectors.
+// If overwrite is true and the file exists, returns -1 without changing it.
+// Otherwise, opens up the image and starts writing. Returns -2 if
+// the image could not be opened, or -3 if there are failures during
+// write, e.g. disk full.
+//
+// wxWindows: This may be called from the gui thread.
+int
+bx_real_sim_c::create_disk_image (
+ const char *filename,
+ int sectors,
+ bx_bool overwrite)
+{
+ FILE *fp;
+ if (!overwrite) {
+ // check for existence first
+ fp = fopen (filename, "r");
+ if (fp) {
+ // yes it exists
+ fclose (fp);
+ return -1;
+ }
+ }
+ fp = fopen (filename, "w");
+ if (fp == NULL) {
+#ifdef HAVE_PERROR
+ char buffer[1024];
+ sprintf (buffer, "while opening '%s' for writing", filename);
+ perror (buffer);
+ // not sure how to get this back into the CI
+#endif
+ return -2;
+ }
+ int sec = sectors;
+ /*
+ * seek to sec*512-1 and write a single character.
+ * can't just do: fseek(fp, 512*sec-1, SEEK_SET)
+ * because 512*sec may be too large for signed int.
+ */
+ while (sec > 0)
+ {
+ /* temp <-- min(sec, 4194303)
+ * 4194303 is (int)(0x7FFFFFFF/512)
+ */
+ int temp = ((sec < 4194303) ? sec : 4194303);
+ fseek(fp, 512*temp, SEEK_CUR);
+ sec -= temp;
+ }
+
+ fseek(fp, -1, SEEK_CUR);
+ if (fputc('\0', fp) == EOF)
+ {
+ fclose (fp);
+ return -3;
+ }
+ fclose (fp);
+ return 0;
+}
+
+void bx_real_sim_c::refresh_ci () {
+ if (SIM->is_wx_selected ()) {
+ // presently, only wxWindows interface uses these events
+ // It's an async event, so allocate a pointer and send it.
+ // The event will be freed by the recipient.
+ BxEvent *event = new BxEvent ();
+ event->type = BX_ASYNC_EVT_REFRESH;
+ sim_to_ci_event (event);
+ }
+}
+
+bx_param_c *
+bx_real_sim_c::get_first_atadevice (Bit32u search_type) {
+ for (int channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if (!bx_options.ata[channel].Opresent->get ())
+ continue;
+ for (int slave=0; slave<2; slave++) {
+ Bit32u present = bx_options.atadevice[channel][slave].Opresent->get ();
+ Bit32u type = bx_options.atadevice[channel][slave].Otype->get ();
+ if (present && (type == search_type)) {
+ return bx_options.atadevice[channel][slave].Omenu;
+ }
+ }
+ }
+ return NULL;
+}
+
+#if BX_DEBUGGER
+
+// this can be safely called from either thread.
+void bx_real_sim_c::debug_break () {
+ bx_debug_break ();
+}
+
+// this should only be called from the sim_thread.
+void bx_real_sim_c::debug_interpret_cmd (char *cmd) {
+ if (!is_sim_thread ()) {
+ fprintf (stderr, "ERROR: debug_interpret_cmd called but not from sim_thread\n");
+ return;
+ }
+ bx_dbg_interpret_line (cmd);
+}
+
+char *bx_real_sim_c::debug_get_next_command ()
+{
+ fprintf (stderr, "begin debug_get_next_command\n");
+ BxEvent event;
+ event.type = BX_SYNC_EVT_GET_DBG_COMMAND;
+ BX_INFO (("asking for next debug command"));
+ sim_to_ci_event (&event);
+ BX_INFO (("received next debug command: '%s'", event.u.debugcmd.command));
+ if (event.retcode >= 0)
+ return event.u.debugcmd.command;
+ return NULL;
+}
+
+void bx_real_sim_c::debug_puts (const char *text)
+{
+ if (SIM->is_wx_selected ()) {
+ // send message to the wxWindows debugger
+ BxEvent *event = new BxEvent ();
+ event->type = BX_ASYNC_EVT_DBG_MSG;
+ event->u.logmsg.msg = text;
+ sim_to_ci_event (event);
+ // the event will be freed by the recipient
+ } else {
+ // text mode debugger: just write to console
+ fputs (text, stderr);
+ delete [] (char *)text;
+ }
+}
+#endif
+
+void
+bx_real_sim_c::register_configuration_interface (
+ const char* name,
+ config_interface_callback_t callback,
+ void *userdata)
+{
+ ci_callback = callback;
+ ci_callback_data = userdata;
+ registered_ci_name = name;
+}
+
+int
+bx_real_sim_c::configuration_interface(const char *ignore, ci_command_t command)
+{
+ bx_param_enum_c *ci_param = SIM->get_param_enum (BXP_SEL_CONFIG_INTERFACE);
+ char *name = ci_param->get_choice (ci_param->get ());
+ if (!ci_callback) {
+ BX_PANIC (("no configuration interface was loaded"));
+ return -1;
+ }
+ if (strcmp (name, registered_ci_name) != 0) {
+ BX_PANIC (("siminterface does not support loading one configuration interface and then calling another"));
+ return -1;
+ }
+ if (!strcmp (name, "wx"))
+ wxsel = true;
+ else
+ wxsel = false;
+ // enter configuration mode, just while running the configuration interface
+ set_display_mode (DISP_MODE_CONFIG);
+ int retval = (*ci_callback)(ci_callback_data, command);
+ set_display_mode (DISP_MODE_SIM);
+ return retval;
+}
+
+int
+bx_real_sim_c::begin_simulation (int argc, char *argv[])
+{
+ return bx_begin_simulation (argc, argv);
+}
+
+bool bx_real_sim_c::is_sim_thread ()
+{
+ if (is_sim_thread_func == NULL) return true;
+ return (*is_sim_thread_func)();
+}
+
+// check if the text console exists. On some platforms, if Bochs is
+// started from the "Start Menu" or by double clicking on it on a Mac,
+// there may be nothing attached to stdin/stdout/stderr. This function
+// tests if stdin/stdout/stderr are usable and returns false if not.
+bool
+bx_real_sim_c::test_for_text_console ()
+{
+#if BX_WITH_CARBON
+ // In a Carbon application, you have a text console if you run the app from
+ // the command line, but if you start it from the finder you don't.
+ if(!isatty(STDIN_FILENO)) return false;
+#endif
+ // default: yes
+ return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+// define methods of bx_param_* and family
+/////////////////////////////////////////////////////////////////////////
+
+bx_object_c::bx_object_c (bx_id id)
+{
+ this->id = id;
+ this->type = BXT_OBJECT;
+}
+
+void
+bx_object_c::set_type (bx_objtype type)
+{
+ this->type = type;
+}
+
+const char* bx_param_c::default_text_format = NULL;
+
+bx_param_c::bx_param_c (bx_id id, char *name, char *description)
+ : bx_object_c (id)
+{
+ set_type (BXT_PARAM);
+ this->name = name;
+ this->description = description;
+ this->text_format = default_text_format;
+ this->ask_format = NULL;
+ this->label = NULL;
+ this->runtime_param = 0;
+ this->enabled = 1;
+ SIM->register_param (id, this);
+}
+
+const char* bx_param_c::set_default_format (const char *f) {
+ const char *old = default_text_format;
+ default_text_format = f;
+ return old;
+}
+
+bx_param_num_c::bx_param_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64s min, Bit64s max, Bit64s initial_val)
+ : bx_param_c (id, name, description)
+{
+ set_type (BXT_PARAM_NUM);
+ this->min = min;
+ this->max = max;
+ this->initial_val = initial_val;
+ this->val.number = initial_val;
+ this->handler = NULL;
+ this->enable_handler = NULL;
+ this->base = default_base;
+ // dependent_list must be initialized before the set(),
+ // because set calls update_dependents().
+ dependent_list = NULL;
+ set (initial_val);
+}
+
+Bit32u bx_param_num_c::default_base = 10;
+
+Bit32u bx_param_num_c::set_default_base (Bit32u val) {
+ Bit32u old = default_base;
+ default_base = val;
+ return old;
+}
+
+void
+bx_param_num_c::reset ()
+{
+ this->val.number = initial_val;
+}
+
+void
+bx_param_num_c::set_handler (param_event_handler handler)
+{
+ this->handler = handler;
+ // now that there's a handler, call set once to run the handler immediately
+ //set (get ());
+}
+
+void
+bx_param_num_c::set_enable_handler (param_enable_handler handler)
+{
+ this->enable_handler = handler;
+}
+
+void bx_param_num_c::set_dependent_list (bx_list_c *l) {
+ dependent_list = l;
+ update_dependents ();
+}
+
+Bit64s
+bx_param_num_c::get64 ()
+{
+ if (handler) {
+ // the handler can decide what value to return and/or do some side effect
+ return (*handler)(this, 0, val.number);
+ } else {
+ // just return the value
+ return val.number;
+ }
+}
+
+void
+bx_param_num_c::set (Bit64s newval)
+{
+ if (handler) {
+ // the handler can override the new value and/or perform some side effect
+ val.number = newval;
+ (*handler)(this, 1, newval);
+ } else {
+ // just set the value. This code does not check max/min.
+ val.number = newval;
+ }
+ if ((val.number < min || val.number > max) && (Bit64u)max != BX_MAX_BIT64U)
+ BX_PANIC (("numerical parameter %s was set to " FMT_LL "d, which is out of range " FMT_LL "d to " FMT_LL "d", get_name (), val.number, min, max));
+ if (dependent_list != NULL) update_dependents ();
+}
+
+void bx_param_num_c::set_range (Bit64u min, Bit64u max)
+{
+ this->min = min;
+ this->max = max;
+}
+
+void bx_param_num_c::set_initial_val (Bit64s initial_val) {
+ this->val.number = this->initial_val = initial_val;
+}
+
+void bx_param_num_c::update_dependents ()
+{
+ if (dependent_list) {
+ int en = val.number && enabled;
+ for (int i=0; i<dependent_list->get_size (); i++) {
+ bx_param_c *param = dependent_list->get (i);
+ if (param != this)
+ param->set_enabled (en);
+ }
+ }
+}
+
+void
+bx_param_num_c::set_enabled (int en)
+{
+ // The enable handler may wish to allow/disallow the action
+ if (enable_handler) {
+ en = (*enable_handler) (this, en);
+ }
+ bx_param_c::set_enabled (en);
+ update_dependents ();
+}
+
+// Signed 64 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64s *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT64S, BX_MAX_BIT64S, *ptr_to_real_val)
+{
+ this->varsize = 16;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p64bit = ptr_to_real_val;
+}
+
+// Unsigned 64 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64u *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT64U, BX_MAX_BIT64U, *ptr_to_real_val)
+{
+ this->varsize = 16;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p64bit = (Bit64s*) ptr_to_real_val;
+}
+
+// Signed 32 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit32s *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT32S, BX_MAX_BIT32S, *ptr_to_real_val)
+{
+ this->varsize = 16;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p32bit = ptr_to_real_val;
+}
+
+// Unsigned 32 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit32u *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT32U, BX_MAX_BIT32U, *ptr_to_real_val)
+{
+ this->varsize = 32;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p32bit = (Bit32s*) ptr_to_real_val;
+}
+
+// Signed 16 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit16s *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT16S, BX_MAX_BIT16S, *ptr_to_real_val)
+{
+ this->varsize = 16;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p16bit = ptr_to_real_val;
+}
+
+// Unsigned 16 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit16u *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT16U, BX_MAX_BIT16U, *ptr_to_real_val)
+{
+ this->varsize = 16;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p16bit = (Bit16s*) ptr_to_real_val;
+}
+
+// Signed 8 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit8s *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT8S, BX_MAX_BIT8S, *ptr_to_real_val)
+{
+ this->varsize = 16;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p8bit = ptr_to_real_val;
+}
+
+// Unsigned 8 bit
+bx_shadow_num_c::bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit8u *ptr_to_real_val,
+ Bit8u highbit,
+ Bit8u lowbit)
+: bx_param_num_c (id, name, description, BX_MIN_BIT8U, BX_MAX_BIT8U, *ptr_to_real_val)
+{
+ this->varsize = 8;
+ this->lowbit = lowbit;
+ this->mask = (1 << (highbit - lowbit)) - 1;
+ val.p8bit = (Bit8s*) ptr_to_real_val;
+}
+
+Bit64s
+bx_shadow_num_c::get64 () {
+ Bit64u current = 0;
+ switch (varsize) {
+ case 8: current = *(val.p8bit); break;
+ case 16: current = *(val.p16bit); break;
+ case 32: current = *(val.p32bit); break;
+ case 64: current = *(val.p64bit); break;
+ default: BX_PANIC(("unsupported varsize %d", varsize));
+ }
+ current = (current >> lowbit) & mask;
+ if (handler) {
+ // the handler can decide what value to return and/or do some side effect
+ return (*handler)(this, 0, current) & mask;
+ } else {
+ // just return the value
+ return current;
+ }
+}
+
+void
+bx_shadow_num_c::set (Bit64s newval)
+{
+ Bit64u tmp = 0;
+ if ((newval < min || newval > max) && (Bit64u)max != BX_MAX_BIT64U)
+ BX_PANIC (("numerical parameter %s was set to " FMT_LL "d, which is out of range " FMT_LL "d to " FMT_LL "d", get_name (), newval, min, max));
+ switch (varsize) {
+ case 8:
+ tmp = (*(val.p8bit) >> lowbit) & mask;
+ tmp |= (newval & mask) << lowbit;
+ *(val.p8bit) = (Bit8s)tmp;
+ break;
+ case 16:
+ tmp = (*(val.p16bit) >> lowbit) & mask;
+ tmp |= (newval & mask) << lowbit;
+ *(val.p16bit) = (Bit16s)tmp;
+ break;
+ case 32:
+ tmp = (*(val.p32bit) >> lowbit) & mask;
+ tmp |= (newval & mask) << lowbit;
+ *(val.p32bit) = (Bit32s)tmp;
+ break;
+ case 64:
+ tmp = (*(val.p64bit) >> lowbit) & mask;
+ tmp |= (newval & mask) << lowbit;
+ *(val.p64bit) = tmp;
+ break;
+ default:
+ BX_PANIC(("unsupported varsize %d", varsize));
+ }
+ if (handler) {
+ // the handler can override the new value and/or perform some side effect
+ (*handler)(this, 1, tmp);
+ }
+}
+
+bx_param_bool_c::bx_param_bool_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64s initial_val)
+ : bx_param_num_c (id, name, description, 0, 1, initial_val)
+{
+ set_type (BXT_PARAM_BOOL);
+ set (initial_val);
+}
+
+bx_shadow_bool_c::bx_shadow_bool_c (bx_id id,
+ char *name,
+ char *description,
+ bx_bool *ptr_to_real_val,
+ Bit8u bitnum)
+ : bx_param_bool_c (id, name, description, (Bit64s) *ptr_to_real_val)
+{
+ val.pbool = ptr_to_real_val;
+ this->bitnum = bitnum;
+}
+
+Bit64s
+bx_shadow_bool_c::get64 () {
+ if (handler) {
+ // the handler can decide what value to return and/or do some side effect
+ Bit64s ret = (*handler)(this, 0, (Bit64s) *(val.pbool));
+ return (ret>>bitnum) & 1;
+ } else {
+ // just return the value
+ return (*(val.pbool)) & 1;
+ }
+}
+
+void
+bx_shadow_bool_c::set (Bit64s newval)
+{
+ // only change the bitnum bit
+ Bit64s tmp = (newval&1) << bitnum;
+ *(val.pbool) &= ~tmp;
+ *(val.pbool) |= tmp;
+ if (handler) {
+ // the handler can override the new value and/or perform some side effect
+ (*handler)(this, 1, newval&1);
+ }
+}
+
+bx_param_enum_c::bx_param_enum_c (bx_id id,
+ char *name,
+ char *description,
+ char **choices,
+ Bit64s initial_val,
+ Bit64s value_base)
+ : bx_param_num_c (id, name, description, value_base, BX_MAX_BIT64S, initial_val)
+{
+ set_type (BXT_PARAM_ENUM);
+ this->choices = choices;
+ // count number of choices, set max
+ char **p = choices;
+ while (*p != NULL) p++;
+ this->min = value_base;
+ // now that the max is known, replace the BX_MAX_BIT64S sent to the parent
+ // class constructor with the real max.
+ this->max = value_base + (p - choices - 1);
+ set (initial_val);
+}
+
+int
+bx_param_enum_c::find_by_name (const char *string)
+{
+ char **p;
+ for (p=&choices[0]; *p; p++) {
+ if (!strcmp (string, *p))
+ return p-choices;
+ }
+ return -1;
+}
+
+bool
+bx_param_enum_c::set_by_name (const char *string)
+{
+ int n = find_by_name (string);
+ if (n<0) return false;
+ set (n);
+ return true;
+}
+
+bx_param_string_c::bx_param_string_c (bx_id id,
+ char *name,
+ char *description,
+ char *initial_val,
+ int maxsize)
+ : bx_param_c (id, name, description)
+{
+ set_type (BXT_PARAM_STRING);
+ if (maxsize < 0)
+ maxsize = strlen(initial_val) + 1;
+ this->val = new char[maxsize];
+ this->initial_val = new char[maxsize];
+ this->handler = NULL;
+ this->enable_handler = NULL;
+ this->maxsize = maxsize;
+ strncpy (this->val, initial_val, maxsize);
+ strncpy (this->initial_val, initial_val, maxsize);
+ this->options = new bx_param_num_c (BXP_NULL,
+ "stringoptions", NULL, 0, BX_MAX_BIT64S, 0);
+ set (initial_val);
+}
+
+bx_param_filename_c::bx_param_filename_c (bx_id id,
+ char *name,
+ char *description,
+ char *initial_val,
+ int maxsize)
+ : bx_param_string_c (id, name, description, initial_val, maxsize)
+{
+ get_options()->set (IS_FILENAME);
+}
+
+bx_param_string_c::~bx_param_string_c ()
+{
+ if ( this->val != NULL )
+ {
+ delete [] this->val;
+ this->val = NULL;
+ }
+ if ( this->initial_val != NULL )
+ {
+ delete [] this->initial_val;
+ this->initial_val = NULL;
+ }
+
+ if ( this->options != NULL )
+ {
+ delete [] this->options;
+ this->options = NULL;
+ }
+}
+
+void
+bx_param_string_c::reset () {
+ strncpy (this->val, this->initial_val, maxsize);
+}
+
+void
+bx_param_string_c::set_handler (param_string_event_handler handler)
+{
+ this->handler = handler;
+ // now that there's a handler, call set once to run the handler immediately
+ //set (getptr ());
+}
+
+void
+bx_param_string_c::set_enable_handler (param_enable_handler handler)
+{
+ this->enable_handler = handler;
+}
+
+void
+bx_param_string_c::set_enabled (int en)
+{
+ // The enable handler may wish to allow/disallow the action
+ if (enable_handler) {
+ en = (*enable_handler) (this, en);
+ }
+ bx_param_c::set_enabled (en);
+}
+
+Bit32s
+bx_param_string_c::get (char *buf, int len)
+{
+ if (options->get () & RAW_BYTES)
+ memcpy (buf, val, len);
+ else
+ strncpy (buf, val, len);
+ if (handler) {
+ // the handler can choose to replace the value in val/len. Also its
+ // return value is passed back as the return value of get.
+ (*handler)(this, 0, buf, len);
+ }
+ return 0;
+}
+
+void
+bx_param_string_c::set (char *buf)
+{
+ if (options->get () & RAW_BYTES)
+ memcpy (val, buf, maxsize);
+ else
+ strncpy (val, buf, maxsize);
+ if (handler) {
+ // the handler can return a different char* to be copied into the value
+ buf = (*handler)(this, 1, buf, -1);
+ }
+}
+
+bx_bool
+bx_param_string_c::equals (const char *buf)
+{
+ if (options->get () & RAW_BYTES)
+ return (memcmp (val, buf, maxsize) == 0);
+ else
+ return (strncmp (val, buf, maxsize) == 0);
+}
+
+bx_list_c::bx_list_c (bx_id id, int maxsize)
+ : bx_param_c (id, "list", "")
+{
+ set_type (BXT_LIST);
+ this->size = 0;
+ this->maxsize = maxsize;
+ this->list = new bx_param_c* [maxsize];
+ init ();
+}
+
+bx_list_c::bx_list_c (bx_id id, char *name, char *description, int maxsize)
+ : bx_param_c (id, name, description)
+{
+ set_type (BXT_LIST);
+ this->size = 0;
+ this->maxsize = maxsize;
+ this->list = new bx_param_c* [maxsize];
+ init ();
+}
+
+bx_list_c::bx_list_c (bx_id id, char *name, char *description, bx_param_c **init_list)
+ : bx_param_c (id, name, description)
+{
+ set_type (BXT_LIST);
+ this->size = 0;
+ while (init_list[this->size] != NULL)
+ this->size++;
+ this->maxsize = this->size;
+ this->list = new bx_param_c* [maxsize];
+ for (int i=0; i<this->size; i++)
+ this->list[i] = init_list[i];
+ init ();
+}
+
+bx_list_c::~bx_list_c()
+{
+ if (this->list)
+ {
+ delete [] this->list;
+ this->list = NULL;
+ }
+ if ( this->title != NULL)
+ {
+ delete this->title;
+ this->title = NULL;
+ }
+ if (this->options != NULL)
+ {
+ delete this->options;
+ this->options = NULL;
+ }
+ if ( this->choice != NULL )
+ {
+ delete this->choice;
+ this->choice = NULL;
+ }
+}
+
+void
+bx_list_c::init ()
+{
+ // the title defaults to the name
+ this->title = new bx_param_string_c (BXP_NULL,
+ "title of list",
+ "",
+ get_name (), 80);
+ this->options = new bx_param_num_c (BXP_NULL,
+ "list_option", "", 0, BX_MAX_BIT64S,
+ 0);
+ this->choice = new bx_param_num_c (BXP_NULL,
+ "list_choice", "", 0, BX_MAX_BIT64S,
+ 1);
+ this->parent = NULL;
+}
+
+bx_list_c *
+bx_list_c::clone ()
+{
+ bx_list_c *newlist = new bx_list_c (BXP_NULL, name, description, maxsize);
+ for (int i=0; i<get_size (); i++)
+ newlist->add (get(i));
+ newlist->set_options (get_options ());
+ newlist->set_parent (get_parent ());
+ return newlist;
+}
+
+void
+bx_list_c::add (bx_param_c *param)
+{
+ if (this->size >= this->maxsize)
+ BX_PANIC (("add param %u to bx_list_c id=%u: list capacity exceeded", param->get_id (), get_id ()));
+ list[size] = param;
+ size++;
+}
+
+bx_param_c *
+bx_list_c::get (int index)
+{
+ BX_ASSERT (index >= 0 && index < size);
+ return list[index];
+}
+
diff --git a/tools/ioemu/gui/siminterface.h b/tools/ioemu/gui/siminterface.h
new file mode 100644
index 0000000000..9a028470c8
--- /dev/null
+++ b/tools/ioemu/gui/siminterface.h
@@ -0,0 +1,1460 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: siminterface.h,v 1.113.2.2 2004/02/06 22:14:35 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Before I can describe what this file is for, I have to make the
+// distinction between a configuration interface (CI) and the VGA display
+// window (VGAW). I will try to avoid the term 'GUI' because it is unclear
+// if that means CI or VGAW, and because not all interfaces are graphical
+// anyway.
+//
+// The traditional Bochs screen is a window with a large VGA display panel and
+// a series of buttons (floppy, cdrom, snapshot, power). Over the years, we
+// have collected many implementations of the VGAW for different environments
+// and platforms; each implementation is in a separate file under gui/*:
+// x.cc, win32.cc, beos.cc, macintosh.cc, etc. The files gui.h and gui.cc
+// define the platform independent part of the VGAW, leaving about 15 methods
+// of the bx_gui_c class undefined. The platform dependent file must
+// implement the remaining 15 methods.
+//
+// The configuration interface is relatively new, started by Bryce Denney in
+// June 2001. The CI is intended to allow the user to edit a variety of
+// configuration and runtime options. Some options, such as memory size or
+// enabling the ethernet card, should only be changed before the simulation
+// begins; others, such as floppy disk image, instructions per second, and
+// logging options can be safely changed at runtime. The CI allows the user to
+// make these changes. Before the CI existed, only a few things could be
+// changed at runtime, all linked to clicking on the VGAW buttons.
+//
+// At the time that the CI was conceived, we were still debating what form the
+// user interface part would take: stdin/stdout menus, a graphical application
+// with menus and dialogs running in a separate thread, or even a tiny web
+// server that you can connect to with a web browser. As a result the
+// interface to the CI was designed so that the user interface of the CI
+// could be replaced easily at compile time, or maybe even at runtime via
+// a plugin architecture. To this end, we kept a clear separation between
+// the user interface code and the siminterface, the code that interfaces with
+// the simulator. The same siminterface is used all the time, while
+// different implementations of the CI can be switched in reasonably easily.
+// Only the CI code uses library specific graphics and I/O functions; the
+// siminterface deals in portable abstractions and callback functions.
+// The first CI implementation was a series of text mode menus implemented in
+// control.cc.
+//
+// The configuration interface MUST use the siminterface methods to access the
+// simulator. It should not modify settings in some device with code like
+// bx_floppy.s.media[2].heads = 17. If such access is needed, then a
+// siminterface method should be written to make the change on the CI's behalf.
+// This separation is enforced by the fact that the CI does not even include
+// bochs.h. You'll notice that control.cc include osdep.h, control.h, and
+// siminterface.h, so it doesn't know what bx_floppy or bx_cpu_c are. I'm sure
+// some people will say is overly restrictive and/or annoying. When I set it
+// up this way, we were still talking about making the CI in a seperate
+// process, where direct method calls would be impossible. Also, we have been
+// considering turning devices into plugin modules which are dynamically
+// linked. Any direct references to something like bx_floppy.s.media[2].heads
+// would have to be reworked before a plugin interface was possible as well.
+//
+// The siminterface is the glue between the CI and the simulator. There is
+// just one global instance of the siminterface object, which can be referred
+// to by the global variable bx_simulator_interface_c *SIM; The base class
+// bx_simulator_interface_c, contains only virtual functions and it defines the
+// interface that the CI is allowed to use. In siminterface.cc, a class
+// called bx_real_sim_c is defined with bx_simulator_interface_c as its parent
+// class. Bx_real_sim_c implements each of the functions. The separation into
+// parent class and child class leaves the possibility of making a different
+// child class that talks to the simulator in a different way (networking for
+// example). If you were writing a user interface in a separate process, you
+// could define a subclass of bx_simulator_interface_c called
+// bx_siminterface_proxy_c which opens up a network port and turns all method
+// calls into network sends and receives. Because the interface is defined
+// entirely by the base class, the code that calls the methods would not know
+// the difference.
+//
+// An important part of the siminterface implementation is the use of parameter
+// classes, or bx_param_*. The parameter classes are described below, where
+// they are declared. Search for "parameter classes" below for detals.
+//
+// Also this header file declares data structures for certain events that pass
+// between the siminterface and the CI. Search for "event structures" below.
+
+
+
+//////////////////////////////////////////////////////
+// BX_UI_TEXT should be set to 1 when the text mode configuration interface
+// is compiled in. This gives each type of parameter a text_print and text_ask
+// method (defined in gui/control.cc) so that you can call text_ask() on any
+// kind of parameter to ask the user to edit the value.
+//
+// I have been considering whether to use the same strategy for the
+// wxWindows interface, but I'm not sure if I like it. One problem is
+// that in order to declare member functions that are useful for
+// wxWindows, the wxWindows header files would have to be included
+// before the param object definitions. That means that all the
+// wxwindows headers would have be included when compiling every
+// single bochs file. One of the things I like about the separation
+// between the simulator and CI is that the two parts can be
+// compiled without any knowledge of the other. Bochs doesn't include
+// <wx.h>, and the wxwindows CI (wxmain.cc) doesn't need to include <bochs.h>.
+// Aside from making compiles faster, this enforces the use of the siminterface
+// so it keeps the interface clean (important when we may have multiple UI
+// implementations for example). This argues for keeping UI-specific
+// structures out of the simulator interface. It certainly works ok for the
+// text interface, but that's because FILE* is standard and portable.
+#define BX_UI_TEXT 1
+
+//////////////////////////////////////////////////////
+
+// list of possible types for bx_param_c and descendant objects
+typedef enum {
+ BXT_OBJECT = 201,
+ BXT_PARAM,
+ BXT_PARAM_NUM,
+ BXT_PARAM_BOOL,
+ BXT_PARAM_ENUM,
+ BXT_PARAM_STRING,
+ BXT_LIST
+} bx_objtype;
+
+// list if parameter id values. The actual values are not important;
+// it's only important that they all be different from each other.
+typedef enum {
+ BXP_NULL = 301,
+ BXP_IPS,
+ BXP_REALTIME_PIT,
+ BXP_TEXT_SNAPSHOT_CHECK,
+ BXP_VGA_UPDATE_INTERVAL,
+ BXP_MOUSE_ENABLED,
+ BXP_MEM_SIZE,
+ BXP_ROM_PATH,
+ BXP_ROM_ADDRESS,
+ BXP_VGA_ROM_PATH,
+ BXP_OPTROM1_PATH,
+ BXP_OPTROM2_PATH,
+ BXP_OPTROM3_PATH,
+ BXP_OPTROM4_PATH,
+ BXP_OPTROM1_ADDRESS,
+ BXP_OPTROM2_ADDRESS,
+ BXP_OPTROM3_ADDRESS,
+ BXP_OPTROM4_ADDRESS,
+ BXP_KBD_SERIAL_DELAY,
+ BXP_KBD_PASTE_DELAY,
+ BXP_KBD_TYPE,
+ BXP_FLOPPY_CMD_DELAY,
+ BXP_FLOPPYA_DEVTYPE,
+ BXP_FLOPPYA_PATH,
+ BXP_FLOPPYA_TYPE,
+ BXP_FLOPPYA_STATUS,
+ BXP_FLOPPYA,
+ BXP_FLOPPYB_DEVTYPE,
+ BXP_FLOPPYB_PATH,
+ BXP_FLOPPYB_TYPE,
+ BXP_FLOPPYB_STATUS,
+ BXP_FLOPPYB,
+
+ BXP_ATA0_MENU,
+ BXP_ATA1_MENU,
+ BXP_ATA2_MENU,
+ BXP_ATA3_MENU,
+#define BXP_ATAx_MENU(i) (BXP_ATA0_MENU + (i))
+ BXP_ATA0,
+ BXP_ATA1,
+ BXP_ATA2,
+ BXP_ATA3,
+#define BXP_ATAx(i) (BXP_ATA0 + (i))
+ BXP_ATA0_PRESENT,
+ BXP_ATA1_PRESENT,
+ BXP_ATA2_PRESENT,
+ BXP_ATA3_PRESENT,
+#define BXP_ATAx_PRESENT(i) (BXP_ATA0_PRESENT + (i))
+ BXP_ATA0_IOADDR1,
+ BXP_ATA1_IOADDR1,
+ BXP_ATA2_IOADDR1,
+ BXP_ATA3_IOADDR1,
+#define BXP_ATAx_IOADDR1(i) (BXP_ATA0_IOADDR1 + (i))
+ BXP_ATA0_IOADDR2,
+ BXP_ATA1_IOADDR2,
+ BXP_ATA2_IOADDR2,
+ BXP_ATA3_IOADDR2,
+#define BXP_ATAx_IOADDR2(i) (BXP_ATA0_IOADDR2 + (i))
+ BXP_ATA0_IRQ,
+ BXP_ATA1_IRQ,
+ BXP_ATA2_IRQ,
+ BXP_ATA3_IRQ,
+#define BXP_ATAx_IRQ(i) (BXP_ATA0_IRQ + (i))
+
+ BXP_ATA0_MASTER,
+ BXP_ATA0_SLAVE,
+ BXP_ATA1_MASTER,
+ BXP_ATA1_SLAVE,
+ BXP_ATA2_MASTER,
+ BXP_ATA2_SLAVE,
+ BXP_ATA3_MASTER,
+ BXP_ATA3_SLAVE,
+#define BXP_ATAx_DEVICE(i, s) (BXP_ATA0_MASTER + (2*(i)) + (s))
+
+#define BXP_PARAMS_PER_ATA_DEVICE 12
+
+ BXP_ATA0_MASTER_PRESENT,
+ BXP_ATA0_SLAVE_PRESENT,
+ BXP_ATA1_MASTER_PRESENT,
+ BXP_ATA1_SLAVE_PRESENT,
+ BXP_ATA2_MASTER_PRESENT,
+ BXP_ATA2_SLAVE_PRESENT,
+ BXP_ATA3_MASTER_PRESENT,
+ BXP_ATA3_SLAVE_PRESENT,
+#define BXP_ATAx_DEVICE_PRESENT(i, s) (BXP_ATA0_MASTER_PRESENT + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_TYPE,
+ BXP_ATA0_SLAVE_TYPE,
+ BXP_ATA1_MASTER_TYPE,
+ BXP_ATA1_SLAVE_TYPE,
+ BXP_ATA2_MASTER_TYPE,
+ BXP_ATA2_SLAVE_TYPE,
+ BXP_ATA3_MASTER_TYPE,
+ BXP_ATA3_SLAVE_TYPE,
+#define BXP_ATAx_DEVICE_TYPE(i, s) (BXP_ATA0_MASTER_TYPE + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_MODE,
+ BXP_ATA0_SLAVE_MODE,
+ BXP_ATA1_MASTER_MODE,
+ BXP_ATA1_SLAVE_MODE,
+ BXP_ATA2_MASTER_MODE,
+ BXP_ATA2_SLAVE_MODE,
+ BXP_ATA3_MASTER_MODE,
+ BXP_ATA3_SLAVE_MODE,
+#define BXP_ATAx_DEVICE_MODE(i, s) (BXP_ATA0_MASTER_MODE + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_PATH,
+ BXP_ATA0_SLAVE_PATH,
+ BXP_ATA1_MASTER_PATH,
+ BXP_ATA1_SLAVE_PATH,
+ BXP_ATA2_MASTER_PATH,
+ BXP_ATA2_SLAVE_PATH,
+ BXP_ATA3_MASTER_PATH,
+ BXP_ATA3_SLAVE_PATH,
+#define BXP_ATAx_DEVICE_PATH(i, s) (BXP_ATA0_MASTER_PATH + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_CYLINDERS,
+ BXP_ATA0_SLAVE_CYLINDERS,
+ BXP_ATA1_MASTER_CYLINDERS,
+ BXP_ATA1_SLAVE_CYLINDERS,
+ BXP_ATA2_MASTER_CYLINDERS,
+ BXP_ATA2_SLAVE_CYLINDERS,
+ BXP_ATA3_MASTER_CYLINDERS,
+ BXP_ATA3_SLAVE_CYLINDERS,
+#define BXP_ATAx_DEVICE_CYLINDERS(i, s) (BXP_ATA0_MASTER_CYLINDERS + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_HEADS,
+ BXP_ATA0_SLAVE_HEADS,
+ BXP_ATA1_MASTER_HEADS,
+ BXP_ATA1_SLAVE_HEADS,
+ BXP_ATA2_MASTER_HEADS,
+ BXP_ATA2_SLAVE_HEADS,
+ BXP_ATA3_MASTER_HEADS,
+ BXP_ATA3_SLAVE_HEADS,
+#define BXP_ATAx_DEVICE_HEADS(i, s) (BXP_ATA0_MASTER_HEADS + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_SPT,
+ BXP_ATA0_SLAVE_SPT,
+ BXP_ATA1_MASTER_SPT,
+ BXP_ATA1_SLAVE_SPT,
+ BXP_ATA2_MASTER_SPT,
+ BXP_ATA2_SLAVE_SPT,
+ BXP_ATA3_MASTER_SPT,
+ BXP_ATA3_SLAVE_SPT,
+#define BXP_ATAx_DEVICE_SPT(i, s) (BXP_ATA0_MASTER_SPT + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_STATUS,
+ BXP_ATA0_SLAVE_STATUS,
+ BXP_ATA1_MASTER_STATUS,
+ BXP_ATA1_SLAVE_STATUS,
+ BXP_ATA2_MASTER_STATUS,
+ BXP_ATA2_SLAVE_STATUS,
+ BXP_ATA3_MASTER_STATUS,
+ BXP_ATA3_SLAVE_STATUS,
+#define BXP_ATAx_DEVICE_STATUS(i, s) (BXP_ATA0_MASTER_STATUS + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_MODEL,
+ BXP_ATA0_SLAVE_MODEL,
+ BXP_ATA1_MASTER_MODEL,
+ BXP_ATA1_SLAVE_MODEL,
+ BXP_ATA2_MASTER_MODEL,
+ BXP_ATA2_SLAVE_MODEL,
+ BXP_ATA3_MASTER_MODEL,
+ BXP_ATA3_SLAVE_MODEL,
+#define BXP_ATAx_DEVICE_MODEL(i, s) (BXP_ATA0_MASTER_MODEL + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_BIOSDETECT,
+ BXP_ATA0_SLAVE_BIOSDETECT,
+ BXP_ATA1_MASTER_BIOSDETECT,
+ BXP_ATA1_SLAVE_BIOSDETECT,
+ BXP_ATA2_MASTER_BIOSDETECT,
+ BXP_ATA2_SLAVE_BIOSDETECT,
+ BXP_ATA3_MASTER_BIOSDETECT,
+ BXP_ATA3_SLAVE_BIOSDETECT,
+#define BXP_ATAx_DEVICE_BIOSDETECT(i, s) (BXP_ATA0_MASTER_BIOSDETECT + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_TRANSLATION,
+ BXP_ATA0_SLAVE_TRANSLATION,
+ BXP_ATA1_MASTER_TRANSLATION,
+ BXP_ATA1_SLAVE_TRANSLATION,
+ BXP_ATA2_MASTER_TRANSLATION,
+ BXP_ATA2_SLAVE_TRANSLATION,
+ BXP_ATA3_MASTER_TRANSLATION,
+ BXP_ATA3_SLAVE_TRANSLATION,
+#define BXP_ATAx_DEVICE_TRANSLATION(i, s) (BXP_ATA0_MASTER_TRANSLATION + (2*(i)) + (s))
+
+ BXP_ATA0_MASTER_JOURNAL,
+ BXP_ATA0_SLAVE_JOURNAL,
+ BXP_ATA1_MASTER_JOURNAL,
+ BXP_ATA1_SLAVE_JOURNAL,
+ BXP_ATA2_MASTER_JOURNAL,
+ BXP_ATA2_SLAVE_JOURNAL,
+ BXP_ATA3_MASTER_JOURNAL,
+ BXP_ATA3_SLAVE_JOURNAL,
+#define BXP_ATAx_DEVICE_JOURNAL(i, s) (BXP_ATA0_MASTER_JOURNAL + (2*(i)) + (s))
+
+#define BXP_PARAMS_PER_SERIAL_PORT 2
+ BXP_COM1_ENABLED,
+ BXP_COM1_PATH,
+ BXP_COM2_ENABLED,
+ BXP_COM2_PATH,
+ BXP_COM3_ENABLED,
+ BXP_COM3_PATH,
+ BXP_COM4_ENABLED,
+ BXP_COM4_PATH,
+#define BXP_PARAMS_PER_USB_HUB 3
+ BXP_USB1_ENABLED,
+ BXP_USB1_IOADDR,
+ BXP_USB1_IRQ,
+ BXP_PRIVATE_COLORMAP,
+ BXP_FULLSCREEN,
+ BXP_SCREENMODE,
+ BXP_I440FX_SUPPORT,
+ BXP_NEWHARDDRIVESUPPORT,
+ BXP_LOG_FILENAME,
+ BXP_LOG_PREFIX,
+ BXP_DEBUGGER_LOG_FILENAME,
+ BXP_CMOS_PATH,
+ BXP_CMOS_IMAGE,
+ BXP_CLOCK,
+ BXP_CLOCK_TIME0,
+ BXP_CLOCK_SYNC,
+ BXP_LOAD32BITOS_WHICH,
+ BXP_LOAD32BITOS_PATH,
+ BXP_LOAD32BITOS_IOLOG,
+ BXP_LOAD32BITOS_INITRD,
+ BXP_LOAD32BITOS,
+ BXP_BOOTDRIVE,
+ BXP_FLOPPYSIGCHECK,
+ BXP_MENU_MAIN,
+ BXP_MENU_MEMORY,
+ BXP_MENU_INTERFACE,
+ BXP_MENU_DISK,
+ BXP_MENU_SERIAL_PARALLEL,
+ BXP_MENU_SOUND,
+ BXP_MENU_KEYBOARD,
+ BXP_MENU_MISC,
+ BXP_MENU_MISC_2,
+ BXP_MENU_RUNTIME,
+ BXP_MAX_IPS,
+ BXP_NE2K_PRESENT,
+ BXP_NE2K_IOADDR,
+ BXP_NE2K_IRQ,
+ BXP_NE2K_MACADDR,
+ BXP_NE2K_ETHMOD,
+ BXP_NE2K_ETHDEV,
+ BXP_NE2K_SCRIPT,
+ BXP_NE2K,
+ BXP_SB16_PRESENT,
+ BXP_SB16_MIDIFILE,
+ BXP_SB16_WAVEFILE,
+ BXP_SB16_LOGFILE,
+ BXP_SB16_MIDIMODE,
+ BXP_SB16_WAVEMODE,
+ BXP_SB16_LOGLEVEL,
+ BXP_SB16_DMATIMER,
+ BXP_SB16,
+#define BXP_PARAMS_PER_PARALLEL_PORT 2
+ BXP_PARPORT1_ENABLED,
+ BXP_PARPORT1_OUTFILE,
+ BXP_PARPORT2_ENABLED,
+ BXP_PARPORT2_OUTFILE,
+ BXP_KEYBOARD_USEMAPPING,
+ BXP_KEYBOARD_MAP,
+ BXP_KEYBOARD,
+ BXP_USER_SHORTCUT,
+ BXP_ASK_FOR_PATHNAME, // for general file selection dialog
+ BXP_BOCHS_START, // How Bochs starts
+ // experiment: add params for CPU registers
+ BXP_CPU_PARAMETERS,
+ BXP_CPU_EAX,
+ BXP_CPU_EBX,
+ BXP_CPU_ECX,
+ BXP_CPU_EDX,
+ BXP_CPU_EBP,
+ BXP_CPU_ESI,
+ BXP_CPU_EDI,
+ BXP_CPU_ESP,
+ BXP_CPU_EIP,
+ BXP_CPU_SEG_CS,
+ BXP_CPU_SEG_DS,
+ BXP_CPU_SEG_SS,
+ BXP_CPU_SEG_ES,
+ BXP_CPU_SEG_FS,
+ BXP_CPU_SEG_GS,
+ BXP_CPU_SEG_LDTR,
+ BXP_CPU_SEG_TR,
+ BXP_CPU_GDTR_BASE,
+ BXP_CPU_GDTR_LIMIT,
+ BXP_CPU_IDTR_BASE,
+ BXP_CPU_IDTR_LIMIT,
+ BXP_CPU_EFLAGS,
+ BXP_CPU_EFLAGS_ID,
+ BXP_CPU_EFLAGS_VIP,
+ BXP_CPU_EFLAGS_VIF,
+ BXP_CPU_EFLAGS_AC,
+ BXP_CPU_EFLAGS_VM,
+ BXP_CPU_EFLAGS_RF,
+ BXP_CPU_EFLAGS_NT,
+ BXP_CPU_EFLAGS_IOPL,
+ BXP_CPU_EFLAGS_OF,
+ BXP_CPU_EFLAGS_DF,
+ BXP_CPU_EFLAGS_IF,
+ BXP_CPU_EFLAGS_TF,
+ BXP_CPU_EFLAGS_SF,
+ BXP_CPU_EFLAGS_ZF,
+ BXP_CPU_EFLAGS_AF,
+ BXP_CPU_EFLAGS_PF,
+ BXP_CPU_EFLAGS_CF,
+ BXP_CPU_DR0,
+ BXP_CPU_DR1,
+ BXP_CPU_DR2,
+ BXP_CPU_DR3,
+ BXP_CPU_DR6,
+ BXP_CPU_DR7,
+ BXP_CPU_TR3,
+ BXP_CPU_TR4,
+ BXP_CPU_TR5,
+ BXP_CPU_TR6,
+ BXP_CPU_TR7,
+ BXP_CPU_CR0,
+ BXP_CPU_CR1,
+ BXP_CPU_CR2,
+ BXP_CPU_CR3,
+ BXP_CPU_CR4,
+ // a few parameters for the keyboard
+ BXP_KBD_PARAMETERS,
+ BXP_KBD_PARE,
+ BXP_KBD_TIM ,
+ BXP_KBD_AUXB,
+ BXP_KBD_KEYL,
+ BXP_KBD_C_D,
+ BXP_KBD_SYSF,
+ BXP_KBD_INPB,
+ BXP_KBD_OUTB,
+ BXP_KBD_TIMER_PENDING,
+ BXP_KBD_IRQ1_REQ,
+ BXP_KBD_IRQ12_REQ,
+#if BX_DEBUGGER
+ // in debugger, is the simulation running (continue command) or waiting.
+ // This is only modified by debugger code, not by the user.
+ BXP_DEBUG_RUNNING,
+#endif
+ BXP_SEL_CONFIG_INTERFACE,
+ BXP_SEL_DISPLAY_LIBRARY,
+ BXP_THIS_IS_THE_LAST // used to determine length of list
+} bx_id;
+
+// use x=1,2,3,4
+#define BXP_COMx_ENABLED(x) \
+ (bx_id)(BXP_COM1_ENABLED + (((x)-1)*BXP_PARAMS_PER_SERIAL_PORT))
+#define BXP_COMx_PATH(x) \
+ (bx_id)(BXP_COM1_PATH + (((x)-1)*BXP_PARAMS_PER_SERIAL_PORT))
+
+// use x=1
+#define BXP_USBx_ENABLED(x) \
+ (bx_id)(BXP_USB1_ENABLED + (((x)-1)*BXP_PARAMS_PER_USB_HUB))
+#define BXP_USBx_IOADDR(x) \
+ (bx_id)(BXP_USB1_IOADDR + (((x)-1)*BXP_PARAMS_PER_USB_HUB))
+#define BXP_USBx_IRQ(x) \
+ (bx_id)(BXP_USB1_IRQ + (((x)-1)*BXP_PARAMS_PER_USB_HUB))
+
+// use x=1,2
+#define BXP_PARPORTx_ENABLED(x) \
+ (bx_id)(BXP_PARPORT1_ENABLED + (((x)-1)*BXP_PARAMS_PER_PARALLEL_PORT))
+#define BXP_PARPORTx_OUTFILE(x) \
+ (bx_id)(BXP_PARPORT1_OUTFILE + (((x)-1)*BXP_PARAMS_PER_PARALLEL_PORT))
+
+typedef enum {
+ BX_TOOLBAR_UNDEFINED,
+ BX_TOOLBAR_FLOPPYA,
+ BX_TOOLBAR_FLOPPYB,
+ BX_TOOLBAR_CDROMD,
+ BX_TOOLBAR_RESET,
+ BX_TOOLBAR_POWER,
+ BX_TOOLBAR_COPY,
+ BX_TOOLBAR_PASTE,
+ BX_TOOLBAR_SNAPSHOT,
+ BX_TOOLBAR_CONFIG,
+ BX_TOOLBAR_MOUSE_EN,
+ BX_TOOLBAR_USER
+} bx_toolbar_buttons;
+
+// Log level defines
+typedef enum {
+ LOGLEV_DEBUG = 0,
+ LOGLEV_INFO,
+ LOGLEV_ERROR,
+ LOGLEV_PANIC,
+ LOGLEV_PASS,
+ N_LOGLEV
+} bx_log_levels;
+
+// types of reset
+#define BX_RESET_SOFTWARE 10
+#define BX_RESET_HARDWARE 11
+
+//cdrom
+#define BX_EJECTED 10
+#define BX_INSERTED 11
+
+// boot devices
+#define BX_BOOT_FLOPPYA 0
+#define BX_BOOT_DISKC 1
+#define BX_BOOT_CDROM 2
+
+// loader hack
+#define Load32bitOSNone 0
+#define Load32bitOSLinux 1
+#define Load32bitOSNullKernel 2 // being developed for plex86
+#define Load32bitOSLast 2
+
+///////////////////////////////////////////////////////////////////
+// event structures for communication between simulator and CI
+///////////////////////////////////////////////////////////////////
+// Because the CI (configuration interface) might be in a different
+// thread or even a different process, we pass events encoded in data
+// structures to it instead of just calling functions. Each type of
+// event is declared as a different structure, and then all those
+// structures are squished into a union in BxEvent. (BTW, this is
+// almost exactly how X windows event structs work.)
+//
+// These are simple structs, unblemished by C++ methods and tricks.
+// No matter what event type it is, we allocate a BxEvent for each
+// one, as opposed to trying to save a few bytes on small events by
+// allocating only the bytes necessary for it. This makes it easy and
+// fast to store events in a queue, like this
+// BxEvent event_queue[MAX_EVENTS];
+//
+// Events come in two varieties: synchronous and asynchronous. We
+// have to worry about sync and async events because the CI and the
+// simulation may be running in different threads. An async event is
+// the simplest. Whichever thread originates the event just builds
+// the data structure, sends it, and then continues with its business.
+// Async events can go in either direction. Synchronous events
+// require the other thread to "respond" before the originating thread
+// can continue. It's like a function with a return value; you can't
+// continue until you get the return value back.
+//
+// Examples:
+//
+// async event: In the wxWindows implementation, both the CI and the
+// VGAW operate in the wxWindows GUI thread. When the user presses a
+// key, wxWindows sends a wxKeyEvent to the VGAW event handler code in
+// wx.cc. The VGAW handler then builds a BxEvent with
+// type=BX_ASYNC_EVT_KEY, and fills in the bx_key and raw_scancode
+// fields. The asynchronous event is placed on the event_queue for
+// the simulator, then the VGAW handler returns. (With wxWindows and
+// many other graphical libaries, the event handler must return
+// quickly because the window will not be updated until it's done.)
+// Some time later, the simulator reaches the point where it checks
+// for new events from the user (actually controlled by
+// bx_keyb_c::periodic() in iodev/keyboard.cc) and calls
+// bx_gui.handle_events(). Then all the events in the queue are
+// processed by the simulator. There is no "response" sent back to
+// the originating thread.
+//
+// sync event: Sometimes the simulator reaches a point where it needs
+// to ask the user how to proceed. In this case, the simulator sends
+// a synchronous event because it requires a response before it can
+// continue. It builds an event structure, perhaps with type
+// BX_SYNC_EVT_ASK_PARAM, sends it to the user interface
+// using the event handler function defined by set_notify_callback(),
+// and pauses the simulation. The user interface asks the user the
+// question, and puts the answer into the BxEvent.retcode field. The
+// event handler function returns the modified BxEvent with retcode
+// filled in, and the simulation continues. The details of this
+// transaction can be complicated if the simulation and CI are not
+// in the same thread, but the behavior is as described.
+//
+
+///// types and definitions used in event structures
+
+#define BX_EVT_IS_ASYNC(type) ((type) > __ALL_EVENTS_BELOW_ARE_ASYNC__)
+
+typedef enum {
+ __ALL_EVENTS_BELOW_ARE_SYNCHRONOUS__ = 2000,
+ BX_SYNC_EVT_GET_PARAM, // CI -> simulator -> CI
+ BX_SYNC_EVT_ASK_PARAM, // simulator -> CI -> simulator
+ BX_SYNC_EVT_TICK, // simulator -> CI, wait for response.
+ BX_SYNC_EVT_LOG_ASK, // simulator -> CI, wait for response.
+ BX_SYNC_EVT_GET_DBG_COMMAND, // simulator -> CI, wait for response.
+ __ALL_EVENTS_BELOW_ARE_ASYNC__,
+ BX_ASYNC_EVT_KEY, // vga window -> simulator
+ BX_ASYNC_EVT_MOUSE, // vga window -> simulator
+ BX_ASYNC_EVT_SET_PARAM, // CI -> simulator
+ BX_ASYNC_EVT_LOG_MSG, // simulator -> CI
+ BX_ASYNC_EVT_DBG_MSG, // simulator -> CI
+ BX_ASYNC_EVT_VALUE_CHANGED, // simulator -> CI
+ BX_ASYNC_EVT_TOOLBAR, // CI -> simulator
+ BX_ASYNC_EVT_REFRESH // simulator -> CI
+} BxEventType;
+
+typedef union {
+ Bit32s s32;
+ char *charptr;
+} AnyParamVal;
+
+// Define substructures which make up the interior of BxEvent. The
+// substructures, such as BxKeyEvent or BxMouseEvent, should never be
+// allocated on their own. They are only intended to be used within
+// the union in the BxEvent structure.
+
+// Event type: BX_SYNC_EVT_TICK
+//
+// A tick event is synchronous, sent from the simulator to the GUI. The
+// event doesn't do anything visible. Primarily it gives the GUI a chance
+// to tell the simulator to quit, if necessary. There may be other uses
+// for the tick in the future, such as giving some kind of regular
+// status report or mentioning watched values that changed, but so far
+// it's just for that one thing. There is no data associated with a
+// tick event.
+
+// Event type: BX_ASYNC_EVT_KEY
+//
+// A key event can be sent from the VGA window to the Bochs simulator.
+// It is asynchronous.
+typedef struct {
+ // what was pressed? This is a BX_KEY_* value. For key releases,
+ // BX_KEY_RELEASED is ORed with the base BX_KEY_*.
+ Bit32u bx_key;
+ bx_bool raw_scancode;
+} BxKeyEvent;
+
+// Event type: BX_ASYNC_EVT_MOUSE
+//
+// A mouse event can be sent from the VGA window to the Bochs
+// simulator. It is asynchronous. Currently unused because mouse
+// events aren't implemented in our wxWindows code yet.
+typedef struct {
+ // type is BX_EVT_MOUSE
+ Bit16s dx, dy; // mouse motion delta
+ Bit8u buttons; // which buttons are pressed.
+ // bit 0: 1=left button down, 0=up
+ // bit 1: 1=right button down, 0=up
+} BxMouseEvent;
+
+// Event type: BX_SYNC_EVT_GET_PARAM, BX_ASYNC_EVT_SET_PARAM
+//
+// Parameter set/get events are initiated by the CI, since Bochs can
+// always access the parameters directly. So far, I haven't used
+// these event types. In the CI I just call
+// SIM->get_param(parameter_id) to get a pointer to the bx_param_c
+// object and then call the get/set methods. This is okay for
+// configuration since bochs is not running. However it could be
+// dangerous for the GUI thread to poke around in Bochs structures
+// while the thread is running. For these cases, I may have to be
+// more careful and actually build get/set events and place them on
+// Bochs's event queue to be processed during SIM->periodic() or
+// something.
+typedef struct {
+ // type is BX_EVT_GET_PARAM, BX_EVT_SET_PARAM
+ class bx_param_c *param; // pointer to param structure
+ AnyParamVal val;
+} BxParamEvent;
+
+// Event type: BX_SYNC_EVT_ASK_PARAM
+// Synchronous event sent from the simulator to the CI. This tells the
+// CI to ask the user to choose the value of a parameter. The CI may
+// need to discover the type of parameter so that it can use the right
+// kind of graphical display. The BxParamEvent is used for these events
+// too.
+// FIXME: at the moment the GUI implements the ASK_PARAM event for just
+// a few parameter types. I need to implement the event for all parameter
+// types.
+
+// Event type: BX_ASYNC_EVT_VALUE_CHANGED
+//
+// Asynchronous event sent from the simulator to the CI, telling it that
+// some value that it (hopefully) cares about has changed. This isn't
+// being used yet, but a good example is in a debugger interface, you might
+// want to maintain a reasonably current display of the PC or some other
+// simulation state. The CI would set some kind of event mask (which
+// doesn't exist now of course) and then when certain values change, the
+// simulator would send this event so that the CI can update. We may need
+// some kind of "flow control" since the simulator will be able to produce
+// new events much faster than the gui can accept them.
+
+// Event type: BX_ASYNC_EVT_LOG_MSG (unused)
+//
+// Asynchronous event from the simulator to the CI. When a BX_PANIC,
+// BX_ERROR, BX_INFO, or BX_DEBUG is found in the simulator code, this
+// event type can be used to inform the CI of the condition. There is
+// no point in sending messages to the CI that will not be displayed; these
+// would only slow the simulation. So we will need some mechanism for
+// choosing what kinds of events will be delivered to the CI. Normally,
+// you wouldn't want to look at the log unless something is going wrong.
+// At that point, you might want to open up a window to watch the debug
+// messages from one or two devices only.
+//
+// Idea: Except for panics that require user attention to continue, it
+// might be most efficient to just append log messages to a file.
+// When the user wants to look at the log messages, the gui can reopen
+// the file (read only), skip to the end, and look backward for a
+// reasonable number of lines to display (200?). This allows it to
+// skip over huge bursts of log entries without allocating memory,
+// synchronizing threads, etc. for each.
+typedef struct {
+ Bit8u level;
+ const char *prefix;
+ const char *msg;
+} BxLogMsgEvent;
+
+// Event type: BX_ASYNC_EVT_DBG_MSG (unused)
+//
+// Also uses BxLogMsgEvent, but this is a message to be displayed in
+// the debugger history window.
+
+// Event type: BX_SYNC_EVT_LOG_ASK
+//
+// This is a synchronous version of BX_ASYNC_EVT_LOG_MSG, which is used
+// when the "action=ask" setting is used. If the simulator runs into a
+// panic, it sends a synchronous BX_SYNC_EVT_LOG_ASK to the CI to be
+// displayed. The CI shows a dialog that asks if the user wants to
+// continue, quit, etc. and sends the answer back to the simulator.
+// This event also uses BxLogMsgEvent.
+enum {
+ BX_LOG_ASK_CHOICE_CONTINUE,
+ BX_LOG_ASK_CHOICE_CONTINUE_ALWAYS,
+ BX_LOG_ASK_CHOICE_DIE,
+ BX_LOG_ASK_CHOICE_DUMP_CORE,
+ BX_LOG_ASK_CHOICE_ENTER_DEBUG,
+ BX_LOG_ASK_N_CHOICES
+};
+
+// Event type: BX_SYNC_EVT_GET_DBG_COMMAND
+//
+// This is a synchronous event sent from the simulator to the debugger
+// requesting the next action. In a text mode debugger, this would prompt
+// the user for the next command. When a new command is ready, the
+// synchronous event is sent back with its fields filled in.
+typedef struct {
+ char *command; // null terminated string. allocated by debugger interface
+ // with new operator, freed by simulator with delete.
+} BxDebugCommand;
+
+
+
+// Event type: BX_EVT_TOOLBAR
+// Asynchronous event from the VGAW to the simulator, sent when the user
+// clicks on a toolbar button. This may one day become something more
+// general, like a command event, but at the moment it's only needed for
+// the toolbar events.
+typedef struct {
+ bx_toolbar_buttons button;
+ bool on; // for toggling buttons, on=true means the toolbar button is
+ // pressed. on=false means it is not pressed.
+} BxToolbarEvent;
+
+// The BxEvent structure should be used for all events. Every event has
+// a type and a spot for a return code (only used for synchronous events).
+typedef struct {
+ BxEventType type; // what kind is this?
+ Bit32s retcode; // sucess or failure. only used for synchronous events.
+ union {
+ BxKeyEvent key;
+ BxMouseEvent mouse;
+ BxParamEvent param;
+ BxLogMsgEvent logmsg;
+ BxToolbarEvent toolbar;
+ BxDebugCommand debugcmd;
+ } u;
+} BxEvent;
+
+
+////////////////////////////////////////////////////////////////////
+// parameter classes: bx_param_c and family
+////////////////////////////////////////////////////////////////////
+//
+// All variables that can be configured through the CI are declared as
+// "parameters" or objects of type bx_param_*. There is a bx_param_*
+// class for each type of data that the user would need to see and
+// edit, e.g. integer, boolean, enum, string, filename, or list of
+// other parameters. The purpose of the bx_param_* class, in addition
+// to storing the parameter's value, is to hold the name, description,
+// and constraints on the value. The bx_param_* class should hold
+// everything that the CI would need to display the value and allow
+// the user to modify it. For integer parameters, the minimum and
+// maximum allowed value can be defined, and the base in which it
+// should be displayed and interpreted. For enums, the
+// bx_param_enum_c structure includes the list of values which the
+// parameter can have.
+//
+// Also, some parameter classes support get/set callback functions to
+// allow arbitrary code to be executed when the parameter is get/set.
+// An example of where this is useful: if you disable the NE2K card,
+// the set() handler for that parameter can tell the user interface
+// that the NE2K's irq, I/O address, and mac address should be
+// disabled (greyed out, hidden, or made inaccessible). The get/set
+// methods can also check if the set() value is acceptable using
+// whatever means and override it.
+//
+// The parameter concept is similar to the use of parameters in JavaBeans.
+
+class bx_object_c;
+class bx_param_c;
+class bx_param_num_c;
+class bx_param_enum_c;
+class bx_param_bool_c;
+class bx_param_string_c;
+class bx_param_filename_c;
+class bx_list_c;
+
+class BOCHSAPI bx_object_c {
+private:
+ bx_id id;
+ bx_objtype type;
+protected:
+ void set_type (bx_objtype type);
+public:
+ bx_object_c (bx_id id);
+ bx_id get_id () { return id; }
+ Bit8u get_type () { return type; }
+};
+
+class BOCHSAPI bx_param_c : public bx_object_c {
+ BOCHSAPI_CYGONLY static const char *default_text_format;
+protected:
+ char *name;
+ char *description;
+ char *label; // label string for text menus and gui dialogs
+ const char *text_format; // printf format string. %d for ints, %s for strings, etc.
+ char *ask_format; // format string for asking for a new value
+ int runtime_param;
+ int enabled;
+public:
+ bx_param_c (bx_id id, char *name, char *description);
+ void set_format (const char *format) {text_format = format;}
+ const char *get_format () {return text_format;}
+ void set_ask_format (char *format) {ask_format = format; }
+ char *get_ask_format () {return ask_format;}
+ void set_label (char *text) {label = text;}
+ char *get_label () {return label;}
+ void set_runtime_param (int val) { runtime_param = val; }
+ int get_runtime_param () { return runtime_param; }
+ char *get_name () { return name; }
+ char *get_description () { return description; }
+ int get_enabled () { return enabled; }
+ virtual void set_enabled (int enabled) { this->enabled = enabled; }
+ void reset () {}
+ int getint () {return -1;}
+ static const char* set_default_format (const char *f);
+ static const char *get_default_format () { return default_text_format; }
+ virtual bx_list_c *get_dependent_list () { return NULL; }
+#if BX_UI_TEXT
+ virtual void text_print (FILE *fp) {}
+ virtual int text_ask (FILE *fpin, FILE *fpout) {return -1;}
+#endif
+};
+
+typedef Bit64s (*param_event_handler)(class bx_param_c *, int set, Bit64s val);
+typedef int (*param_enable_handler)(class bx_param_c *, int en);
+
+class BOCHSAPI bx_param_num_c : public bx_param_c {
+ BOCHSAPI_CYGONLY static Bit32u default_base;
+ // The dependent_list is initialized to NULL. If dependent_list is modified
+ // to point to a bx_list_c of other parameters, the set() method of
+ // bx_param_bool_c will enable those parameters when this bool is true, and
+ // disable them when this bool is false.
+ bx_list_c *dependent_list;
+ void update_dependents ();
+protected:
+ Bit64s min, max, initial_val;
+ union _uval_ {
+ Bit64s number; // used by bx_param_num_c
+ Bit64s *p64bit; // used by bx_shadow_num_c
+ Bit32s *p32bit; // used by bx_shadow_num_c
+ Bit16s *p16bit; // used by bx_shadow_num_c
+ Bit8s *p8bit; // used by bx_shadow_num_c
+ bx_bool *pbool; // used by bx_shadow_bool_c
+ } val;
+ param_event_handler handler;
+ param_enable_handler enable_handler;
+ int base;
+ Bit32u options;
+public:
+ enum {
+ // When a bx_param_num_c is displayed in dialog, USE_SPIN_CONTROL controls
+ // whether a spin control should be used instead of a simple text control.
+ USE_SPIN_CONTROL = (1<<0)
+ } bx_numopt_bits;
+ bx_param_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64s min, Bit64s max, Bit64s initial_val);
+ void reset ();
+ void set_handler (param_event_handler handler);
+ void set_enable_handler (param_enable_handler handler);
+ virtual bx_list_c *get_dependent_list () { return dependent_list; }
+ void set_dependent_list (bx_list_c *l);
+ virtual void set_enabled (int enabled);
+ virtual Bit32s get () { return (Bit32s) get64(); }
+ virtual Bit64s get64 ();
+ virtual void set (Bit64s val);
+ void set_base (int base) { this->base = base; }
+ void set_initial_val (Bit64s initial_val);
+ int get_base () { return base; }
+ void set_range (Bit64u min, Bit64u max);
+ Bit64s get_min () { return min; }
+ Bit64s get_max () { return max; }
+ static Bit32u set_default_base (Bit32u val);
+ static Bit32u get_default_base () { return default_base; }
+ void set_options (Bit32u options) { this->options = options; }
+ Bit32u get_options () { return options; }
+#if BX_UI_TEXT
+ virtual void text_print (FILE *fp);
+ virtual int text_ask (FILE *fpin, FILE *fpout);
+#endif
+};
+
+// a bx_shadow_num_c is like a bx_param_num_c except that it doesn't
+// store the actual value with its data. Instead, it uses val.p32bit
+// to keep a pointer to the actual data. This is used to register
+// existing variables as parameters, without have to access it via
+// set/get methods.
+class BOCHSAPI bx_shadow_num_c : public bx_param_num_c {
+ Bit8u varsize; // must be 64, 32, 16, or 8
+ Bit8u lowbit; // range of bits associated with this param
+ Bit64u mask; // mask is ANDed with value before it is returned from get
+public:
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64s *ptr_to_real_val,
+ Bit8u highbit = 63,
+ Bit8u lowbit = 0);
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64u *ptr_to_real_val,
+ Bit8u highbit = 63,
+ Bit8u lowbit = 0);
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit32s *ptr_to_real_val,
+ Bit8u highbit = 31,
+ Bit8u lowbit = 0);
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit32u *ptr_to_real_val,
+ Bit8u highbit = 31,
+ Bit8u lowbit = 0);
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit16s *ptr_to_real_val,
+ Bit8u highbit = 15,
+ Bit8u lowbit = 0);
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit16u *ptr_to_real_val,
+ Bit8u highbit = 15,
+ Bit8u lowbit = 0);
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit8s *ptr_to_real_val,
+ Bit8u highbit = 7,
+ Bit8u lowbit = 0);
+ bx_shadow_num_c (bx_id id,
+ char *name,
+ char *description,
+ Bit8u *ptr_to_real_val,
+ Bit8u highbit = 7,
+ Bit8u lowbit = 0);
+ virtual Bit64s get64 ();
+ virtual void set (Bit64s val);
+};
+
+class BOCHSAPI bx_param_bool_c : public bx_param_num_c {
+ // many boolean variables are used to enable/disable modules. In the
+ // user interface, the enable variable should enable/disable all the
+ // other parameters associated with that module.
+public:
+ bx_param_bool_c (bx_id id,
+ char *name,
+ char *description,
+ Bit64s initial_val);
+#if BX_UI_TEXT
+ virtual void text_print (FILE *fp);
+ virtual int text_ask (FILE *fpin, FILE *fpout);
+#endif
+};
+
+// a bx_shadow_bool_c is a shadow param based on bx_param_bool_c.
+class BOCHSAPI bx_shadow_bool_c : public bx_param_bool_c {
+ // each bit of a bitfield can be a separate value. bitnum tells which
+ // bit is used. get/set will only modify that bit.
+ Bit8u bitnum;
+public:
+ bx_shadow_bool_c (bx_id id,
+ char *name,
+ char *description,
+ bx_bool *ptr_to_real_val,
+ Bit8u bitnum = 0);
+ virtual Bit64s get64 ();
+ virtual void set (Bit64s val);
+};
+
+
+class BOCHSAPI bx_param_enum_c : public bx_param_num_c {
+ char **choices;
+public:
+ bx_param_enum_c (bx_id id,
+ char *name,
+ char *description,
+ char **choices,
+ Bit64s initial_val,
+ Bit64s value_base = 0);
+ char *get_choice (int n) { return choices[n]; }
+ int find_by_name (const char *string);
+ bool set_by_name (const char *string);
+#if BX_UI_TEXT
+ virtual void text_print (FILE *fp);
+ virtual int text_ask (FILE *fpin, FILE *fpout);
+#endif
+};
+
+typedef char* (*param_string_event_handler)(class bx_param_string_c *, int set, char *val, int maxlen);
+
+class BOCHSAPI bx_param_string_c : public bx_param_c {
+ int maxsize;
+ char *val, *initial_val;
+ param_string_event_handler handler;
+ param_enable_handler enable_handler;
+ bx_param_num_c *options;
+ char separator;
+public:
+ enum {
+ RAW_BYTES = 1, // use binary text editor, like MAC addr
+ IS_FILENAME = 2, // 1=yes it's a filename, 0=not a filename.
+ // Some guis have a file browser. This
+ // bit suggests that they use it.
+ SAVE_FILE_DIALOG = 4 // Use save dialog opposed to open file dialog
+ } bx_string_opt_bits;
+ bx_param_string_c (bx_id id,
+ char *name,
+ char *description,
+ char *initial_val,
+ int maxsize=-1);
+ virtual ~bx_param_string_c ();
+ void reset ();
+ void set_handler (param_string_event_handler handler);
+ void set_enable_handler (param_enable_handler handler);
+ virtual void set_enabled (int enabled);
+ Bit32s get (char *buf, int len);
+ char *getptr () {return val; }
+ void set (char *buf);
+ bx_bool equals (const char *buf);
+ bx_param_num_c *get_options () { return options; }
+ void set_separator (char sep) {separator = sep; }
+ char get_separator () {return separator; }
+ int get_maxsize () {return maxsize; }
+#if BX_UI_TEXT
+ virtual void text_print (FILE *fp);
+ virtual int text_ask (FILE *fpin, FILE *fpout);
+#endif
+};
+
+// Declare a filename class. It is identical to a string, except that
+// it initializes the options differently. This is just a shortcut
+// for declaring a string param and setting the options with IS_FILENAME.
+class BOCHSAPI bx_param_filename_c : public bx_param_string_c {
+public:
+ bx_param_filename_c (bx_id id,
+ char *name,
+ char *description,
+ char *initial_val,
+ int maxsize=-1);
+};
+
+class BOCHSAPI bx_list_c : public bx_param_c {
+private:
+ // just a list of bx_param_c objects. size tells current number of
+ // objects in the list, and maxsize tells how many list items are
+ // allocated in the constructor.
+ bx_param_c **list;
+ int size, maxsize;
+ // options is a bit field whose bits are defined by bx_listopt_bits ORed
+ // together. Options is a bx_param so that if necessary the bx_list could
+ // install a handler to cause get/set of options to have side effects.
+ bx_param_num_c *options;
+ // for a menu, the value of choice before the call to "ask" is default.
+ // After ask, choice holds the value that the user chose. Choice defaults
+ // to 1 in the constructor.
+ bx_param_num_c *choice;
+ // title of the menu or series
+ bx_param_string_c *title;
+ // if the menu shows a "return to previous menu" type of choice,
+ // this controls where that choice will go.
+ bx_param_c *parent;
+ void init ();
+public:
+ enum {
+ // When a bx_list_c is displayed as a menu, SHOW_PARENT controls whether or
+ // not the menu shows a "Return to parent menu" choice or not.
+ SHOW_PARENT = (1<<0),
+ // Some lists are best displayed shown as menus, others as a series of
+ // related questions. This bit suggests to the CI that the series of
+ // questions format is preferred.
+ SERIES_ASK = (1<<1),
+ // When a bx_list_c is displayed in a dialog, USE_TAB_WINDOW suggests
+ // to the CI that each item in the list should be shown as a separate
+ // tab. This would be most appropriate when each item is another list
+ // of parameters.
+ USE_TAB_WINDOW = (1<<2),
+ // When a bx_list_c is displayed in a dialog, the list name is used as the
+ // label of the group box if USE_BOX_TITLE is set. This is only necessary if
+ // more than one list appears in a dialog box.
+ USE_BOX_TITLE = (1<<3)
+ } bx_listopt_bits;
+ bx_list_c (bx_id id, int maxsize);
+ bx_list_c (bx_id id, char *name, char *description, bx_param_c **init_list);
+ bx_list_c (bx_id id, char *name, char *description, int maxsize);
+ virtual ~bx_list_c();
+ bx_list_c *clone ();
+ void add (bx_param_c *param);
+ bx_param_c *get (int index);
+ int get_size () { return size; }
+ bx_param_num_c *get_options () { return options; }
+ void set_options (bx_param_num_c *newopt) { options = newopt; }
+ bx_param_num_c *get_choice () { return choice; }
+ bx_param_string_c *get_title () { return title; }
+ void set_parent (bx_param_c *newparent) { parent = newparent; }
+ bx_param_c *get_parent () { return parent; }
+#if BX_UI_TEXT
+ virtual void text_print (FILE *);
+ virtual int text_ask (FILE *fpin, FILE *fpout);
+#endif
+};
+
+////////////////////////////////////////////////////////////////
+
+
+// These are the different start modes.
+enum {
+ // Just start the simulation without running the configuration interface
+ // at all, unless something goes wrong.
+ BX_QUICK_START = 200,
+ // Run the configuration interface. The default action will be to load a
+ // configuration file. This makes sense if a config file could not be
+ // loaded, either because it wasn't found or because it had errors.
+ BX_LOAD_START,
+ // Run the configuration interface. The default action will be to
+ // edit the configuration.
+ BX_EDIT_START,
+ // Run the configuration interface, but make the default action be to
+ // start the simulation.
+ BX_RUN_START
+};
+
+#define BX_FLOPPY_NONE 10 // floppy not present
+#define BX_FLOPPY_1_2 11 // 1.2M 5.25"
+#define BX_FLOPPY_1_44 12 // 1.44M 3.5"
+#define BX_FLOPPY_2_88 13 // 2.88M 3.5"
+#define BX_FLOPPY_720K 14 // 720K 3.5"
+#define BX_FLOPPY_360K 15 // 360K 5.25"
+#define BX_FLOPPY_160K 16 // 160K 5.25"
+#define BX_FLOPPY_180K 17 // 180K 5.25"
+#define BX_FLOPPY_320K 18 // 320K 5.25"
+#define BX_FLOPPY_LAST 18 // last legal value of floppy type
+
+#define BX_FLOPPY_GUESS 20 // decide based on image size
+
+#define BX_ATA_DEVICE_DISK 0
+#define BX_ATA_DEVICE_CDROM 1
+#define BX_ATA_DEVICE_LAST 1
+
+#define BX_ATA_BIOSDETECT_NONE 0
+#define BX_ATA_BIOSDETECT_AUTO 1
+#define BX_ATA_BIOSDETECT_CMOS 2
+
+#define BX_ATA_TRANSLATION_NONE 0
+#define BX_ATA_TRANSLATION_LBA 1
+#define BX_ATA_TRANSLATION_LARGE 2
+#define BX_ATA_TRANSLATION_RECHS 3
+#define BX_ATA_TRANSLATION_AUTO 4
+#define BX_ATA_TRANSLATION_LAST 4
+
+#define BX_ATA_MODE_FLAT 0
+#define BX_ATA_MODE_CONCAT 1
+#define BX_ATA_MODE_EXTDISKSIM 2
+#define BX_ATA_MODE_DLL_HD 3
+#define BX_ATA_MODE_SPARSE 4
+#define BX_ATA_MODE_VMWARE3 5
+#define BX_ATA_MODE_UNDOABLE 6
+#define BX_ATA_MODE_GROWING 7
+#define BX_ATA_MODE_VOLATILE 8
+#define BX_ATA_MODE_LAST 8
+//#define BX_ATA_MODE_Z_UNDOABLE 9
+//#define BX_ATA_MODE_Z_VOLATILE 10
+//#define BX_ATA_MODE_SPLIT 6
+
+#define BX_CLOCK_SYNC_NONE 0
+#define BX_CLOCK_SYNC_REALTIME 1
+#define BX_CLOCK_SYNC_SLOWDOWN 2
+#define BX_CLOCK_SYNC_BOTH 3
+#define BX_CLOCK_SYNC_LAST 3
+
+#define BX_CLOCK_TIME0_LOCAL 1
+#define BX_CLOCK_TIME0_UTC 2
+
+BOCHSAPI extern char *bochs_start_names[];
+BOCHSAPI extern int n_bochs_start_names;
+BOCHSAPI extern char *floppy_type_names[];
+BOCHSAPI extern int floppy_type_n_sectors[];
+BOCHSAPI extern int n_floppy_type_names;
+BOCHSAPI extern char *floppy_status_names[];
+BOCHSAPI extern int n_floppy_status_names;
+BOCHSAPI extern char *floppy_bootdisk_names[];
+BOCHSAPI extern int n_floppy_bootdisk_names;
+BOCHSAPI extern char *loader_os_names[];
+BOCHSAPI extern int n_loader_os_names;
+BOCHSAPI extern char *keyboard_type_names[];
+BOCHSAPI extern int n_keyboard_type_names;
+BOCHSAPI extern char *atadevice_type_names[];
+BOCHSAPI extern int n_atadevice_type_names;
+BOCHSAPI extern char *atadevice_mode_names[];
+BOCHSAPI extern int n_atadevice_mode_names;
+BOCHSAPI extern char *atadevice_status_names[];
+BOCHSAPI extern int n_atadevice_status_names;
+BOCHSAPI extern char *atadevice_biosdetect_names[];
+BOCHSAPI extern int n_atadevice_biosdetect_names;
+BOCHSAPI extern char *atadevice_translation_names[];
+BOCHSAPI extern int n_atadevice_translation_names;
+BOCHSAPI extern char *clock_sync_names[];
+BOCHSAPI extern int clock_sync_n_names;
+
+typedef struct {
+ bx_param_enum_c *Odevtype;
+ bx_param_string_c *Opath;
+ bx_param_enum_c *Otype;
+ bx_param_enum_c *Ostatus;
+ } bx_floppy_options;
+
+typedef struct {
+ bx_list_c *Omenu;
+ bx_param_bool_c *Opresent;
+ bx_param_enum_c *Otype;
+ bx_param_enum_c *Omode;
+ bx_param_string_c *Opath;
+ bx_param_string_c *Ojournal;
+ bx_param_num_c *Ocylinders;
+ bx_param_num_c *Oheads;
+ bx_param_num_c *Ospt;
+ bx_param_enum_c *Ostatus;
+ bx_param_string_c *Omodel;
+ bx_param_enum_c *Obiosdetect;
+ bx_param_enum_c *Otranslation;
+ } bx_atadevice_options;
+
+typedef struct {
+ bx_param_bool_c *Oenabled;
+ bx_param_string_c *Odev;
+ } bx_serial_options;
+
+typedef struct {
+ bx_param_bool_c *Oenabled;
+ bx_param_num_c *Oioaddr;
+ bx_param_num_c *Oirq;
+ } bx_usb_options;
+
+
+////////////////////////////////////////////////////////////////////
+// base class simulator interface, contains just virtual functions.
+// I'm not longer sure that having a base class is going to be of any
+// use... -Bryce
+
+#include <setjmp.h>
+
+enum ci_command_t { CI_START, CI_RUNTIME_CONFIG, CI_SHUTDOWN };
+enum ci_return_t {
+ CI_OK, // normal return value
+ CI_ERR_NO_TEXT_CONSOLE // err: can't work because there's no text console
+ };
+typedef int (*config_interface_callback_t)(void *userdata, ci_command_t command);
+
+// bx_gui->set_display_mode() changes the mode between the configuration
+// interface and the simulation. This is primarily intended for display
+// libraries which have a full-screen mode such as SDL, term, and svgalib. The
+// display mode is set to DISP_MODE_CONFIG before displaying any configuration
+// menus, for panics that requires user input, when entering the debugger, etc.
+// It is set to DISP_MODE_SIM when the Bochs simulation resumes. The constants
+// are defined here so that configuration interfaces can use them with the
+// bx_simulator_interface_c::set_display_mode() method.
+enum disp_mode_t { DISP_MODE_CONFIG=100, DISP_MODE_SIM };
+
+class BOCHSAPI bx_simulator_interface_c {
+public:
+ bx_simulator_interface_c ();
+ virtual void set_quit_context (jmp_buf *context) {}
+ virtual int get_init_done () { return -1; }
+ virtual int set_init_done (int n) {return -1;}
+ virtual void get_param_id_range (int *min, int *max) {}
+ virtual int register_param (bx_id id, bx_param_c *it) {return -1;}
+ virtual void reset_all_param () {}
+ virtual bx_param_c *get_param (bx_id id) {return NULL;}
+ virtual bx_param_num_c *get_param_num (bx_id id) {return NULL;}
+ virtual bx_param_string_c *get_param_string (bx_id id) {return NULL;}
+ virtual bx_param_bool_c *get_param_bool (bx_id id) {return NULL;}
+ virtual bx_param_enum_c *get_param_enum (bx_id id) {return NULL;}
+ virtual int get_n_log_modules () {return -1;}
+ virtual char *get_prefix (int mod) {return 0;}
+ virtual int get_log_action (int mod, int level) {return -1;}
+ virtual void set_log_action (int mod, int level, int action) {}
+ virtual int get_default_log_action (int level) {return -1;}
+ virtual void set_default_log_action (int level, int action) {}
+ virtual char *get_action_name (int action) {return 0;}
+ virtual const char *get_log_level_name (int level) {return 0;}
+ virtual int get_max_log_level () {return -1;}
+
+ // exiting is somewhat complicated! The preferred way to exit bochs is
+ // to call BX_EXIT(exitcode). That is defined to call
+ // SIM->quit_sim(exitcode). The quit_sim function first calls
+ // the cleanup functions in bochs so that it can destroy windows
+ // and free up memory, then sends a notify message to the CI
+ // telling it that bochs has stopped.
+ virtual void quit_sim (int code) {}
+
+ virtual int get_exit_code () { return 0; }
+
+ virtual int get_default_rc (char *path, int len) {return -1;}
+ virtual int read_rc (char *path) {return -1;}
+ virtual int write_rc (char *rc, int overwrite) {return -1;}
+ virtual int get_log_file (char *path, int len) {return -1;}
+ virtual int set_log_file (char *path) {return -1;}
+ virtual int get_log_prefix (char *prefix, int len) {return -1;}
+ virtual int set_log_prefix (char *prefix) {return -1;}
+ virtual int get_debugger_log_file (char *path, int len) {return -1;}
+ virtual int set_debugger_log_file (char *path) {return -1;}
+ virtual int get_floppy_options (int drive, bx_floppy_options *out) {return -1;}
+ virtual int get_cdrom_options (int drive, bx_atadevice_options *out, int *where = NULL) {return -1;}
+ virtual char *get_floppy_type_name (int type) {return NULL;}
+
+ // The CI calls set_notify_callback to register its event handler function.
+ // This event handler function is called whenever the simulator needs to
+ // send an event to the CI. For example, if the simulator hits a panic and
+ // wants to ask the user how to proceed, it would call the CI event handler
+ // to ask the CI to display a dialog.
+ //
+ // NOTE: At present, the standard VGAW buttons (floppy, snapshot, power,
+ // etc.) are displayed and handled by gui.cc, not by the CI or siminterface.
+ // gui.cc uses its own callback functions to implement the behavior of
+ // the buttons. Some of these implementations call the siminterface.
+ typedef BxEvent* (*bxevent_handler)(void *theclass, BxEvent *event);
+ virtual void set_notify_callback (bxevent_handler func, void *arg) {}
+ virtual void get_notify_callback (bxevent_handler *func, void **arg) {}
+
+ // send an event from the simulator to the CI.
+ virtual BxEvent* sim_to_ci_event (BxEvent *event) {return NULL;}
+
+ // called from simulator when it hits serious errors, to ask if the user
+ // wants to continue or not
+ virtual int log_msg (const char *prefix, int level, const char *msg) {return -1;}
+
+ // tell the CI to ask the user for the value of a parameter.
+ virtual int ask_param (bx_id param) {return -1;}
+
+ // ask the user for a pathname
+ virtual int ask_filename (char *filename, int maxlen, char *prompt, char *the_default, int flags) {return -1;}
+ // called at a regular interval, currently by the keyboard handler.
+ virtual void periodic () {}
+ virtual int create_disk_image (const char *filename, int sectors, bx_bool overwrite) {return -3;}
+ // Tell the configuration interface (CI) that some parameter values have
+ // changed. The CI will reread the parameters and change its display if it's
+ // appropriate. Maybe later: mention which params have changed to save time.
+ virtual void refresh_ci () {}
+ // forces a vga update. This was added so that a debugger can force
+ // a vga update when single stepping, without having to wait thousands
+ // of cycles for the normal vga refresh triggered by iodev/keyboard.cc.
+ virtual void refresh_vga () {}
+ // forces a call to bx_gui.handle_events. This was added so that a debugger
+ // can force the gui events to be handled, so that interactive things such
+ // as a toolbar click will be processed.
+ virtual void handle_events () {}
+ // return first hard disk in ATA interface
+ virtual bx_param_c *get_first_cdrom () {return NULL;}
+ // return first cdrom in ATA interface
+ virtual bx_param_c *get_first_hd () {return NULL;}
+#if BX_DEBUGGER
+ // for debugger: same behavior as pressing control-C
+ virtual void debug_break () {}
+ virtual void debug_interpret_cmd (char *cmd) {}
+ virtual char *debug_get_next_command () {return NULL;}
+ virtual void debug_puts (const char *text) {}
+#endif
+ virtual void register_configuration_interface (
+ const char* name,
+ config_interface_callback_t callback,
+ void *userdata) {}
+ virtual int configuration_interface(const char* name, ci_command_t command) {return -1; }
+ virtual int begin_simulation (int argc, char *argv[]) {return -1;}
+ typedef bool (*is_sim_thread_func_t)();
+ is_sim_thread_func_t is_sim_thread_func;
+ virtual void set_sim_thread_func (is_sim_thread_func_t func) {
+ is_sim_thread_func = func;
+ }
+ virtual bool is_sim_thread () {return true;}
+ virtual bool is_wx_selected () {return false;}
+ // provide interface to bx_gui->set_display_mode() method for config
+ // interfaces to use.
+ virtual void set_display_mode (disp_mode_t newmode) {}
+ virtual bool test_for_text_console () { return true; }
+};
+
+BOCHSAPI extern bx_simulator_interface_c *SIM;
+
+BOCHSAPI extern void bx_init_siminterface ();
+BOCHSAPI extern int bx_init_main (int argc, char *argv[]);
+
+#if defined(__WXMSW__) || defined(WIN32)
+// Just to provide HINSTANCE, etc. in files that have not included bochs.h.
+// I don't like this at all, but I don't see a way around it.
+#include <windows.h>
+#endif
+
+// define structure to hold data that is passed into our main function.
+typedef struct BOCHSAPI {
+ // standard argc,argv
+ int argc;
+ char **argv;
+#ifdef WIN32
+ char initial_dir[MAX_PATH];
+#endif
+#ifdef __WXMSW__
+ // these are only used when compiling with wxWindows. This gives us a
+ // place to store the data that was passed to WinMain.
+ HINSTANCE hInstance;
+ HINSTANCE hPrevInstance;
+ LPSTR m_lpCmdLine;
+ int nCmdShow;
+#endif
+} bx_startup_flags_t;
+
+BOCHSAPI extern bx_startup_flags_t bx_startup_flags;
+BOCHSAPI extern bx_bool bx_user_quit;
diff --git a/tools/ioemu/gui/svga.cc b/tools/ioemu/gui/svga.cc
new file mode 100644
index 0000000000..33020fca80
--- /dev/null
+++ b/tools/ioemu/gui/svga.cc
@@ -0,0 +1,514 @@
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#define _MULTI_THREAD
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_WITH_SVGA
+
+#include <stdlib.h>
+#include </usr/include/vga.h>
+#include <vgagl.h>
+#include <vgakeyboard.h>
+#include <vgamouse.h>
+
+#include "font/vga.bitmap.h"
+//#include "icon_bochs.h"
+
+class bx_svga_gui_c : public bx_gui_c {
+public:
+ bx_svga_gui_c (void);
+ DECLARE_GUI_VIRTUAL_METHODS()
+ virtual void set_display_mode (disp_mode_t newmode);
+};
+
+// declare one instance of the gui object and call macro to insert the
+// plugin code
+static bx_svga_gui_c *theGui = NULL;
+
+IMPLEMENT_GUI_PLUGIN_CODE(svga)
+
+#define LOG_THIS theGui->
+
+static unsigned res_x, res_y;
+static int fontwidth = 8, fontheight = 16;
+static unsigned tilewidth, tileheight;
+static unsigned char vgafont[256 * 16];
+static int clut8 = 0;
+GraphicsContext *screen = NULL;
+static int save_vga_mode;
+static int save_vga_pal[256 * 3];
+
+void keyboard_handler(int scancode, int press);
+void mouse_handler(int button, int dx, int dy, int dz,
+ int drx, int dry, int drz);
+
+unsigned char reverse_byteorder(unsigned char b)
+{
+ unsigned char ret = 0;
+
+ for (unsigned i=0;i<8;i++){
+ ret |= (b & 0x01) << (7 - i);
+ b >>= 1;
+ }
+ return ret;
+}
+
+void create_vga_font()
+{
+ memcpy(vgafont, bx_vgafont, sizeof(bx_vgafont));
+
+ for (unsigned i=0;i< sizeof(bx_vgafont);i++) {
+ vgafont[i] = reverse_byteorder(vgafont[i]);
+ }
+}
+
+bx_svga_gui_c::bx_svga_gui_c ()
+{
+ put("SVGA");
+}
+
+void bx_svga_gui_c::specific_init(
+ int argc,
+ char **argv,
+ unsigned x_tilesize,
+ unsigned y_tilesize,
+ unsigned header_bar_y)
+{
+ tilewidth = x_tilesize;
+ tileheight = y_tilesize;
+
+ if(vga_init() != 0 )
+ {
+ LOG_THIS setonoff(LOGLEV_PANIC, ACT_FATAL);
+ BX_PANIC (("Unable to initialize SVGAlib"));
+ return;
+ }
+
+ screen = gl_allocatecontext();
+
+ dimension_update(640,400);
+ create_vga_font();
+ gl_setfont(8, 16, (void *)vgafont);
+ gl_setwritemode(FONT_COMPRESSED);
+
+ keyboard_init();
+ keyboard_seteventhandler((__keyboard_handler) keyboard_handler);
+
+ vga_setmousesupport(1);
+ mouse_seteventhandler((__mouse_handler) mouse_handler);
+ if (vga_ext_set(VGA_EXT_AVAILABLE, VGA_AVAIL_FLAGS) & VGA_CLUT8) {
+ vga_ext_set(VGA_EXT_SET, VGA_CLUT8);
+ clut8 = 1;
+ }
+ // Save settings to prepare for mode transition in set_display_mode.
+ // If DISP_MODE_SIM is called first, these values will be used.
+ save_vga_mode = vga_getcurrentmode();
+ vga_getpalvec(0, 256, save_vga_pal);
+}
+
+void bx_svga_gui_c::text_update(
+ Bit8u *old_text,
+ Bit8u *new_text,
+ unsigned long cursor_x,
+ unsigned long cursor_y,
+ bx_vga_tminfo_t tm_info,
+ unsigned rows)
+{
+ unsigned x, y, i;
+ unsigned chars, cols;
+ char s[] = " ";
+ static unsigned int previ;
+ unsigned int cursori;
+
+ cols = res_x/fontwidth;
+
+ cursori = (cursor_y*cols + cursor_x) * 2;
+
+ chars = cols*rows;
+
+ for (i=0; i<chars*2; i+=2) {
+ if (i == cursori || i == previ || old_text[i] != new_text[i] ||
+ old_text[i+1] != new_text[i+1]) {
+
+ s[0] = new_text[i];
+ x = (i/2) % cols;
+ y = (i/2) / cols;
+
+ if (i == cursori) {
+ gl_setfontcolors(new_text[i+1] & 0x0F, (new_text[i+1] & 0xF0) >> 4);
+ } else {
+ gl_setfontcolors((new_text[i+1] & 0xF0) >> 4, new_text[i+1] & 0x0F);
+ }
+ gl_write(x * fontwidth, y * fontheight, s);
+ }
+ }
+ previ = cursori;
+}
+
+ int
+bx_svga_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes)
+{
+ return 0;
+}
+
+ int
+bx_svga_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len)
+{
+ return 0;
+}
+
+
+void bx_svga_gui_c::graphics_tile_update(
+ Bit8u *snapshot,
+ unsigned x,
+ unsigned y)
+{
+ gl_putbox(x, y, tilewidth, tileheight, snapshot);
+}
+
+static Bit32u vga_to_bx_key(int key)
+{
+ switch (key) {
+ case SCANCODE_ESCAPE: return BX_KEY_ESC;
+ case SCANCODE_1: return BX_KEY_1;
+ case SCANCODE_2: return BX_KEY_2;
+ case SCANCODE_3: return BX_KEY_3;
+ case SCANCODE_4: return BX_KEY_4;
+ case SCANCODE_5: return BX_KEY_5;
+ case SCANCODE_6: return BX_KEY_6;
+ case SCANCODE_7: return BX_KEY_7;
+ case SCANCODE_8: return BX_KEY_8;
+ case SCANCODE_9: return BX_KEY_9;
+ case SCANCODE_0: return BX_KEY_0;
+
+ case SCANCODE_MINUS: return BX_KEY_MINUS;
+ case SCANCODE_EQUAL: return BX_KEY_EQUALS;
+ case SCANCODE_TAB: return BX_KEY_TAB;
+ case SCANCODE_BACKSPACE: return BX_KEY_BACKSPACE;
+
+ case SCANCODE_Q: return BX_KEY_Q;
+ case SCANCODE_W: return BX_KEY_W;
+ case SCANCODE_E: return BX_KEY_E;
+ case SCANCODE_R: return BX_KEY_R;
+ case SCANCODE_T: return BX_KEY_T;
+ case SCANCODE_Y: return BX_KEY_Y;
+ case SCANCODE_U: return BX_KEY_U;
+ case SCANCODE_I: return BX_KEY_I;
+ case SCANCODE_O: return BX_KEY_O;
+ case SCANCODE_P: return BX_KEY_P;
+
+ case SCANCODE_BRACKET_LEFT: return BX_KEY_LEFT_BRACKET;
+ case SCANCODE_BRACKET_RIGHT: return BX_KEY_RIGHT_BRACKET;
+
+ case SCANCODE_ENTER: return BX_KEY_ENTER;
+ case SCANCODE_LEFTCONTROL: return BX_KEY_CTRL_L;
+
+ case SCANCODE_A: return BX_KEY_A;
+ case SCANCODE_S: return BX_KEY_S;
+ case SCANCODE_D: return BX_KEY_D;
+ case SCANCODE_F: return BX_KEY_F;
+ case SCANCODE_G: return BX_KEY_G;
+ case SCANCODE_H: return BX_KEY_H;
+ case SCANCODE_J: return BX_KEY_J;
+ case SCANCODE_K: return BX_KEY_K;
+ case SCANCODE_L: return BX_KEY_L;
+
+ case SCANCODE_SEMICOLON: return BX_KEY_SEMICOLON;
+ case SCANCODE_APOSTROPHE: return BX_KEY_SINGLE_QUOTE;
+ case SCANCODE_GRAVE: return BX_KEY_GRAVE;
+
+ case SCANCODE_LEFTSHIFT: return BX_KEY_SHIFT_L;
+ case SCANCODE_BACKSLASH: return BX_KEY_BACKSLASH;
+
+ case SCANCODE_Z: return BX_KEY_Z;
+ case SCANCODE_X: return BX_KEY_X;
+ case SCANCODE_C: return BX_KEY_C;
+ case SCANCODE_V: return BX_KEY_V;
+ case SCANCODE_B: return BX_KEY_B;
+ case SCANCODE_N: return BX_KEY_N;
+ case SCANCODE_M: return BX_KEY_M;
+
+ case SCANCODE_COMMA: return BX_KEY_COMMA;
+ case SCANCODE_PERIOD: return BX_KEY_PERIOD;
+ case SCANCODE_SLASH: return BX_KEY_SLASH;
+
+ case SCANCODE_RIGHTSHIFT: return BX_KEY_SHIFT_R;
+ case SCANCODE_KEYPADMULTIPLY: return BX_KEY_KP_MULTIPLY;
+
+ case SCANCODE_LEFTALT: return BX_KEY_ALT_L;
+ case SCANCODE_SPACE: return BX_KEY_SPACE;
+ case SCANCODE_CAPSLOCK: return BX_KEY_CAPS_LOCK;
+
+ case SCANCODE_F1: return BX_KEY_F1;
+ case SCANCODE_F2: return BX_KEY_F2;
+ case SCANCODE_F3: return BX_KEY_F3;
+ case SCANCODE_F4: return BX_KEY_F4;
+ case SCANCODE_F5: return BX_KEY_F5;
+ case SCANCODE_F6: return BX_KEY_F6;
+ case SCANCODE_F7: return BX_KEY_F7;
+ case SCANCODE_F8: return BX_KEY_F8;
+ case SCANCODE_F9: return BX_KEY_F9;
+ case SCANCODE_F10: return BX_KEY_F10;
+
+ case SCANCODE_NUMLOCK: return BX_KEY_NUM_LOCK;
+ case SCANCODE_SCROLLLOCK: return BX_KEY_SCRL_LOCK;
+
+ case SCANCODE_KEYPAD7: return BX_KEY_KP_HOME;
+ case SCANCODE_KEYPAD8: return BX_KEY_KP_UP;
+ case SCANCODE_KEYPAD9: return BX_KEY_KP_PAGE_UP;
+ case SCANCODE_KEYPADMINUS: return BX_KEY_KP_SUBTRACT;
+ case SCANCODE_KEYPAD4: return BX_KEY_KP_LEFT;
+ case SCANCODE_KEYPAD5: return BX_KEY_KP_5;
+ case SCANCODE_KEYPAD6: return BX_KEY_KP_RIGHT;
+ case SCANCODE_KEYPADPLUS: return BX_KEY_KP_ADD;
+ case SCANCODE_KEYPAD1: return BX_KEY_KP_END;
+ case SCANCODE_KEYPAD2: return BX_KEY_KP_DOWN;
+ case SCANCODE_KEYPAD3: return BX_KEY_KP_PAGE_DOWN;
+ case SCANCODE_KEYPAD0: return BX_KEY_KP_INSERT;
+// case SCANCODE_KEYPADPERIOD: return BX_KEY_KP_; /* ??? */
+
+// case SCANCODE_LESS: return BX_KEY_KP_LESS; /* ??? */
+
+ case SCANCODE_F11: return BX_KEY_F11;
+ case SCANCODE_F12: return BX_KEY_F12;
+
+ case SCANCODE_KEYPADENTER: return BX_KEY_KP_ENTER;
+ case SCANCODE_RIGHTCONTROL: return BX_KEY_CTRL_R;
+ case SCANCODE_KEYPADDIVIDE: return BX_KEY_KP_DIVIDE;
+ case SCANCODE_PRINTSCREEN: return BX_KEY_PRINT;
+ case SCANCODE_RIGHTALT: return BX_KEY_ALT_R;
+ case SCANCODE_BREAK: return BX_KEY_PAUSE;
+
+ case SCANCODE_HOME: return BX_KEY_HOME;
+ case SCANCODE_CURSORBLOCKUP: return BX_KEY_UP;
+ case SCANCODE_PAGEUP: return BX_KEY_PAGE_UP;
+ case SCANCODE_CURSORBLOCKLEFT: return BX_KEY_LEFT;
+ case SCANCODE_CURSORBLOCKRIGHT: return BX_KEY_RIGHT;
+ case SCANCODE_END: return BX_KEY_END;
+ case SCANCODE_CURSORBLOCKDOWN: return BX_KEY_DOWN;
+ case SCANCODE_PAGEDOWN: return BX_KEY_PAGE_DOWN;
+ case SCANCODE_INSERT: return BX_KEY_INSERT;
+ case SCANCODE_REMOVE: return BX_KEY_DELETE;
+
+ case SCANCODE_RIGHTWIN: return BX_KEY_WIN_R;
+ case SCANCODE_LEFTWIN: return BX_KEY_WIN_L;
+
+ default: return 0;
+ }
+}
+
+void keyboard_handler(int scancode, int press)
+{
+ if (scancode != SCANCODE_F12) {
+ int bx_key = vga_to_bx_key(scancode);
+ Bit32u key_state;
+
+ if (press) {
+ key_state = BX_KEY_PRESSED;
+ } else {
+ key_state = BX_KEY_RELEASED;
+ }
+
+ DEV_kbd_gen_scancode(bx_key | key_state);
+ } else {
+ BX_INFO(("F12 pressed"));
+ // show runtime options menu, which uses stdin/stdout
+ SIM->configuration_interface (NULL, CI_RUNTIME_CONFIG);
+ }
+}
+
+void mouse_handler(int button, int dx, int dy, int dz,
+ int drx, int dry, int drz)
+{
+ int buttons = 0;
+
+ if (button & MOUSE_LEFTBUTTON) {
+ buttons |= 0x01;
+ }
+
+ if (button & MOUSE_RIGHTBUTTON) {
+ buttons |= 0x02;
+ }
+ DEV_mouse_motion((int) (0.25 * dx), (int) -(0.25 * dy), buttons);
+}
+
+void bx_svga_gui_c::handle_events(void)
+{
+ keyboard_update();
+ keyboard_clearstate();
+ mouse_update();
+}
+
+void bx_svga_gui_c::flush(void)
+{
+ gl_copyscreen(screen);
+}
+
+void bx_svga_gui_c::clear_screen(void)
+{
+ gl_clearscreen(0);
+}
+
+bx_bool bx_svga_gui_c::palette_change(
+ unsigned index,
+ unsigned red,
+ unsigned green,
+ unsigned blue)
+{
+ if( index > 255 ) return 0;
+
+ // without VGA_CLUT8 extension we have only 6 bits for each r,g,b value
+ if (!clut8 && (red > 63 || green > 63 || blue > 63)) {
+ red = red >> 2;
+ green = green >> 2;
+ blue = blue >> 2;
+ }
+
+ vga_setpalette(index, red, green, blue);
+
+ return 1;
+}
+
+
+void bx_svga_gui_c::dimension_update(
+ unsigned x,
+ unsigned y,
+ unsigned fheight,
+ unsigned fwidth,
+ unsigned bpp)
+{
+ int newmode = 0;
+
+ if (bpp > 8) {
+ BX_PANIC(("%d bpp graphics mode not supported yet", bpp));
+ }
+ if( fheight > 0 )
+ {
+ fontheight = fheight;
+ if (fwidth != 8) {
+ x = x * 8 / fwidth;
+ }
+ fontwidth = 8;
+ }
+
+ if( (x == res_x) && (y == res_y )) return;
+
+ if (x == 640 && y == 480) {
+ newmode = G640x480x256;
+ } else if (x == 640 && y == 400) {
+ newmode = G640x400x256;
+ } else if (x == 320 && y == 200) {
+ newmode = G320x200x256;
+ }
+
+ if (!vga_hasmode(newmode)) {
+ newmode = G640x480x256; // trying "default" mode...
+ }
+
+ if (vga_setmode(newmode) != 0)
+ {
+ LOG_THIS setonoff(LOGLEV_PANIC, ACT_FATAL);
+ BX_PANIC (("Unable to set requested videomode: %ix%i", x, y));
+ }
+
+ gl_setcontextvga(newmode);
+ gl_getcontext(screen);
+ gl_setcontextvgavirtual(newmode);
+ save_vga_mode = newmode;
+
+ res_x = x;
+ res_y = y;
+}
+
+
+unsigned bx_svga_gui_c::create_bitmap(
+ const unsigned char *bmap,
+ unsigned xdim,
+ unsigned ydim)
+{
+ return 0;
+}
+
+
+unsigned bx_svga_gui_c::headerbar_bitmap(
+ unsigned bmap_id,
+ unsigned alignment,
+ void (*f)(void))
+{
+ return 0;
+}
+
+
+void bx_svga_gui_c::replace_bitmap(
+ unsigned hbar_id,
+ unsigned bmap_id)
+{
+}
+
+
+void bx_svga_gui_c::show_headerbar(void)
+{
+}
+
+
+void bx_svga_gui_c::mouse_enabled_changed_specific (bx_bool val)
+{
+}
+
+
+void headerbar_click(int x)
+{
+}
+
+void bx_svga_gui_c::exit(void)
+{
+ vga_setmode(TEXT);
+ keyboard_close();
+ mouse_close();
+}
+
+void
+bx_svga_gui_c::set_display_mode (disp_mode_t newmode)
+{
+ // if no mode change, do nothing.
+ if (disp_mode == newmode) return;
+ // remember the display mode for next time
+ disp_mode = newmode;
+ switch (newmode) {
+ case DISP_MODE_CONFIG:
+ BX_DEBUG (("switch to configuration mode (back to console)"));
+ // remember old values and switch to text mode
+ save_vga_mode = vga_getcurrentmode();
+ vga_getpalvec(0, 256, save_vga_pal);
+ keyboard_close();
+ vga_setmode(TEXT);
+ break;
+ case DISP_MODE_SIM:
+ BX_DEBUG (("switch to simulation mode (fullscreen)"));
+ keyboard_init();
+ keyboard_seteventhandler((__keyboard_handler) keyboard_handler);
+ vga_setmode(save_vga_mode);
+ vga_setpalvec(0, 256, save_vga_pal);
+ break;
+ }
+}
+
+#endif /* if BX_WITH_SVGA */
diff --git a/tools/ioemu/gui/term.cc b/tools/ioemu/gui/term.cc
new file mode 100644
index 0000000000..37bf9c5936
--- /dev/null
+++ b/tools/ioemu/gui/term.cc
@@ -0,0 +1,843 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: term.cc,v 1.31 2003/08/17 23:40:38 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2000 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_WITH_TERM
+
+#include "icon_bochs.h"
+
+extern "C" {
+#include <curses.h>
+#include <signal.h>
+};
+
+class bx_term_gui_c : public bx_gui_c {
+public:
+ bx_term_gui_c (void) {}
+ DECLARE_GUI_VIRTUAL_METHODS()
+
+ virtual Bit32u get_sighandler_mask ();
+ // called when registered signal arrives
+ virtual void sighandler (int sig);
+};
+
+// declare one instance of the gui object and call macro to insert the
+// plugin code
+static bx_term_gui_c *theGui = NULL;
+IMPLEMENT_GUI_PLUGIN_CODE(term)
+
+#define LOG_THIS theGui->
+
+bx_bool initialized = 0;
+static unsigned int text_cols = 80, text_rows = 25;
+
+static short curses_color[8] = {
+ /* 0 */ COLOR_BLACK,
+ /* 1 */ COLOR_BLUE,
+ /* 2 */ COLOR_GREEN,
+ /* 3 */ COLOR_CYAN,
+ /* 4 */ COLOR_RED,
+ /* 5 */ COLOR_MAGENTA,
+ /* 6 */ COLOR_YELLOW,
+ /* 7 */ COLOR_WHITE
+};
+
+static chtype vga_to_term[128] = {
+ 0xc7, 0xfc, 0xe9, 0xe2, 0xe4, 0xe0, 0xe5, 0xe7,
+ 0xea, 0xeb, 0xe8, 0xef, 0xee, 0xec, 0xc4, 0xc5,
+ 0xc9, 0xe6, 0xc6, 0xf4, 0xf6, 0xf2, 0xfb, 0xf9,
+ 0xff, 0xd6, 0xdc, 0xe7, 0xa3, 0xa5, ' ', ' ',
+ 0xe1, 0xed, 0xf3, 0xfa, 0xf1, 0xd1, 0xaa, 0xba,
+ 0xbf, ' ', 0xac, ' ', ' ', 0xa1, 0xab, 0xbb,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, ' ', ' ', ' ', ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', 0xb5, ' ',
+ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+ ' ', 0xb1, ' ', ' ', ' ', ' ', 0xf7, ' ',
+ 0xb0, ' ', ' ', ' ', ' ', 0xb2, ' ', ' '
+};
+
+static void
+do_scan(int key_event, int shift, int ctrl, int alt)
+{
+ /* XXX At some point, cache alt/ctrl/shift so only when the state
+ changes do we simulate a press or release, to cut down on
+ keyboard input to the simulated machine */
+
+ BX_DEBUG(("key_event %d/0x%x %s%s%s",
+ key_event,key_event,
+ shift?"(shift)":"",
+ ctrl?"(ctrl)":"",
+ alt?"(alt)":""));
+ if(shift)
+ DEV_kbd_gen_scancode(BX_KEY_SHIFT_L);
+ if(ctrl)
+ DEV_kbd_gen_scancode(BX_KEY_CTRL_L);
+ if(alt)
+ DEV_kbd_gen_scancode(BX_KEY_ALT_L);
+ DEV_kbd_gen_scancode(key_event);
+ key_event |= BX_KEY_RELEASED;
+
+ DEV_kbd_gen_scancode(key_event);
+ if(alt)
+ DEV_kbd_gen_scancode(BX_KEY_ALT_L|BX_KEY_RELEASED);
+ if(ctrl)
+ DEV_kbd_gen_scancode(BX_KEY_CTRL_L|BX_KEY_RELEASED);
+ if(shift)
+ DEV_kbd_gen_scancode(BX_KEY_SHIFT_L|BX_KEY_RELEASED);
+}
+
+Bit32u
+bx_term_gui_c::get_sighandler_mask ()
+{
+ return
+ (1<<SIGHUP)
+ | (1<<SIGINT)
+ | (1<<SIGQUIT)
+#ifdef SIGSTOP
+ | (1<<SIGSTOP)
+#endif
+#ifdef SIGTSTP
+ | (1<<SIGTSTP)
+#endif
+ | (1<<SIGTERM);
+}
+
+void
+bx_term_gui_c::sighandler(int signo)
+{
+ switch(signo) {
+ case SIGINT:
+ do_scan(BX_KEY_C,0,1,0);
+ break;
+#ifdef SIGSTOP
+ case SIGSTOP:
+ do_scan(BX_KEY_S,0,1,0);
+ break;
+#endif
+#ifdef SIGTSTP
+ case SIGTSTP:
+ do_scan(BX_KEY_Z,0,1,0);
+ break;
+#endif
+ default:
+ BX_INFO(("sig %d caught",signo));
+ break;
+ }
+}
+
+// ::SPECIFIC_INIT()
+//
+// Called from gui.cc, once upon program startup, to allow for the
+// specific GUI code (X11, BeOS, ...) to be initialized.
+//
+// argc, argv: not used right now, but the intention is to pass native GUI
+// specific options from the command line. (X11 options, BeOS options,...)
+//
+// tilewidth, tileheight: for optimization, graphics_tile_update() passes
+// only updated regions of the screen to the gui code to be redrawn.
+// These define the dimensions of a region (tile).
+// headerbar_y: A headerbar (toolbar) is display on the top of the
+// VGA window, showing floppy status, and other information. It
+// always assumes the width of the current VGA mode width, but
+// it's height is defined by this parameter.
+
+ void
+bx_term_gui_c::specific_init(int argc, char **argv, unsigned tilewidth, unsigned tileheight,
+ unsigned headerbar_y)
+{
+ put("TGUI");
+ UNUSED(argc);
+ UNUSED(argv);
+ UNUSED(tilewidth);
+ UNUSED(tileheight);
+ UNUSED(headerbar_y);
+
+ UNUSED(bochs_icon_bits); // global variable
+
+ // the ask menu causes trouble
+ io->set_log_action(LOGLEV_PANIC, ACT_FATAL);
+ // logfile should be different from stderr, otherwise terminal mode
+ // really ends up having fun
+ if (!strcmp(bx_options.log.Ofilename->getptr(), "-"))
+ BX_PANIC(("cannot log to stderr in term mode"));
+
+ initscr();
+ start_color();
+ cbreak();
+ curs_set(2);
+ keypad(stdscr,TRUE);
+ nodelay(stdscr, TRUE);
+ noecho();
+
+#if BX_HAVE_COLOR_SET
+ if (has_colors()) {
+ for (int i=0; i<COLORS; i++) {
+ for (int j=0; j<COLORS; j++) {
+ if ((i!=0)||(j!=0)) init_pair(i * COLORS + j, j, i);
+ }
+ }
+ }
+#endif
+
+ if (bx_options.Oprivate_colormap->get ())
+ BX_ERROR(("WARNING: private_colormap option ignored."));
+ initialized = 1;
+}
+
+
+
+void
+do_char(int character,int alt)
+{
+ switch (character) {
+ // control keys
+ case 0x9: do_scan(BX_KEY_TAB,0,0,alt); break;
+ case 0xa: do_scan(BX_KEY_KP_ENTER,0,0,alt); break;
+ case 0xd: do_scan(BX_KEY_KP_DELETE,0,0,alt); break;
+ case 0x1: do_scan(BX_KEY_A,0,1,alt); break;
+ case 0x2: do_scan(BX_KEY_B,0,1,alt); break;
+ case 0x3: do_scan(BX_KEY_C,0,1,alt); break;
+ case 0x4: do_scan(BX_KEY_D,0,1,alt); break;
+ case 0x5: do_scan(BX_KEY_E,0,1,alt); break;
+ case 0x6: do_scan(BX_KEY_F,0,1,alt); break;
+ case 0x7: do_scan(BX_KEY_G,0,1,alt); break;
+ case 0x8: do_scan(BX_KEY_H,0,1,alt); break;
+ case 0xb: do_scan(BX_KEY_K,0,1,alt); break;
+ case 0xc: do_scan(BX_KEY_L,0,1,alt); break;
+ case 0xe: do_scan(BX_KEY_N,0,1,alt); break;
+ case 0xf: do_scan(BX_KEY_O,0,1,alt); break;
+ case 0x10: do_scan(BX_KEY_P,0,1,alt); break;
+ case 0x11: do_scan(BX_KEY_Q,0,1,alt); break;
+ case 0x12: do_scan(BX_KEY_R,0,1,alt); break;
+ case 0x13: do_scan(BX_KEY_S,0,1,alt); break;
+ case 0x14: do_scan(BX_KEY_T,0,1,alt); break;
+ case 0x15: do_scan(BX_KEY_U,0,1,alt); break;
+ case 0x16: do_scan(BX_KEY_V,0,1,alt); break;
+ case 0x17: do_scan(BX_KEY_W,0,1,alt); break;
+ case 0x18: do_scan(BX_KEY_X,0,1,alt); break;
+ case 0x19: do_scan(BX_KEY_Y,0,1,alt); break;
+ case 0x1a: do_scan(BX_KEY_Z,0,1,alt); break;
+ case 0x20: do_scan(BX_KEY_SPACE,0,0,alt); break;
+ case 0x107: do_scan(BX_KEY_BACKSPACE,0,0,alt); break;
+ case 0x102: do_scan(BX_KEY_DOWN,0,0,alt); break;
+ case 0x103: do_scan(BX_KEY_UP,0,0,alt); break;
+ case 0x104: do_scan(BX_KEY_LEFT,0,0,alt); break;
+ case 0x105: do_scan(BX_KEY_RIGHT,0,0,alt); break;
+ case 0x152: do_scan(BX_KEY_PAGE_DOWN,0,0,alt); break;
+ case 0x153: do_scan(BX_KEY_PAGE_UP,0,0,alt); break;
+ case 0x106: do_scan(BX_KEY_HOME,0,0,alt); break;
+ case 0x168: do_scan(BX_KEY_END,0,0,alt); break;
+ case 0x14b: do_scan(BX_KEY_INSERT,0,0,alt); break;
+ case 0x7f: do_scan(BX_KEY_DELETE,0,0,alt); break;
+ case 0x1b: do_scan(BX_KEY_ESC,0,0,alt); break;
+ case '!': do_scan(BX_KEY_1,1,0,alt); break;
+ case '\'': do_scan(BX_KEY_SINGLE_QUOTE,0,0,alt); break;
+ case '#': do_scan(BX_KEY_3,1,0,alt); break;
+ case '$': do_scan(BX_KEY_4,1,0,alt); break;
+ case '%': do_scan(BX_KEY_5,1,0,alt); break;
+ case '^': do_scan(BX_KEY_6,1,0,alt); break;
+ case '&': do_scan(BX_KEY_7,1,0,alt); break;
+ case '"': do_scan(BX_KEY_SINGLE_QUOTE,1,0,alt); break;
+
+ // ()*+,-./
+ case '(': do_scan(BX_KEY_9,1,0,alt); break;
+ case ')': do_scan(BX_KEY_0,1,0,alt); break;
+ case '*': do_scan(BX_KEY_8,1,0,alt); break;
+ case '+': do_scan(BX_KEY_EQUALS,1,0,alt); break;
+ case ',': do_scan(BX_KEY_COMMA,0,0,alt); break;
+ case '-': do_scan(BX_KEY_MINUS,0,0,alt); break;
+ case '.': do_scan(BX_KEY_PERIOD,0,0,alt); break;
+ case '/': do_scan(BX_KEY_SLASH,0,0,alt); break;
+
+ // 01234567
+ case '0': do_scan(BX_KEY_0,0,0,alt); break;
+ case '1': do_scan(BX_KEY_1,0,0,alt); break;
+ case '2': do_scan(BX_KEY_2,0,0,alt); break;
+ case '3': do_scan(BX_KEY_3,0,0,alt); break;
+ case '4': do_scan(BX_KEY_4,0,0,alt); break;
+ case '5': do_scan(BX_KEY_5,0,0,alt); break;
+ case '6': do_scan(BX_KEY_6,0,0,alt); break;
+ case '7': do_scan(BX_KEY_7,0,0,alt); break;
+
+ // 89:;<=>?
+ case '8': do_scan(BX_KEY_8,0,0,alt); break;
+ case '9': do_scan(BX_KEY_9,0,0,alt); break;
+ case ':': do_scan(BX_KEY_SEMICOLON,1,0,alt); break;
+ case ';': do_scan(BX_KEY_SEMICOLON,0,0,alt); break;
+ case '<': do_scan(BX_KEY_COMMA,1,0,alt); break;
+ case '=': do_scan(BX_KEY_EQUALS,0,0,alt); break;
+ case '>': do_scan(BX_KEY_PERIOD,1,0,alt); break;
+ case '?': do_scan(BX_KEY_SLASH,1,0,alt); break;
+
+ // @ABCDEFG
+ case '@': do_scan(BX_KEY_2,1,0,alt); break;
+ case 'A': do_scan(BX_KEY_A,1,0,alt); break;
+ case 'B': do_scan(BX_KEY_B,1,0,alt); break;
+ case 'C': do_scan(BX_KEY_C,1,0,alt); break;
+ case 'D': do_scan(BX_KEY_D,1,0,alt); break;
+ case 'E': do_scan(BX_KEY_E,1,0,alt); break;
+ case 'F': do_scan(BX_KEY_F,1,0,alt); break;
+ case 'G': do_scan(BX_KEY_G,1,0,alt); break;
+
+
+ // HIJKLMNO
+ case 'H': do_scan(BX_KEY_H,1,0,alt); break;
+ case 'I': do_scan(BX_KEY_I,1,0,alt); break;
+ case 'J': do_scan(BX_KEY_J,1,0,alt); break;
+ case 'K': do_scan(BX_KEY_K,1,0,alt); break;
+ case 'L': do_scan(BX_KEY_L,1,0,alt); break;
+ case 'M': do_scan(BX_KEY_M,1,0,alt); break;
+ case 'N': do_scan(BX_KEY_N,1,0,alt); break;
+ case 'O': do_scan(BX_KEY_O,1,0,alt); break;
+
+
+ // PQRSTUVW
+ case 'P': do_scan(BX_KEY_P,1,0,alt); break;
+ case 'Q': do_scan(BX_KEY_Q,1,0,alt); break;
+ case 'R': do_scan(BX_KEY_R,1,0,alt); break;
+ case 'S': do_scan(BX_KEY_S,1,0,alt); break;
+ case 'T': do_scan(BX_KEY_T,1,0,alt); break;
+ case 'U': do_scan(BX_KEY_U,1,0,alt); break;
+ case 'V': do_scan(BX_KEY_V,1,0,alt); break;
+ case 'W': do_scan(BX_KEY_W,1,0,alt); break;
+
+ // XYZ[\]^_
+ case 'X': do_scan(BX_KEY_X,1,0,alt); break;
+ case 'Y': do_scan(BX_KEY_Y,1,0,alt); break;
+ case 'Z': do_scan(BX_KEY_Z,1,0,alt); break;
+ case '{': do_scan(BX_KEY_LEFT_BRACKET,1,0,alt); break;
+ case '|': do_scan(BX_KEY_BACKSLASH,1,0,alt); break;
+ case '}': do_scan(BX_KEY_RIGHT_BRACKET,1,0,alt); break;
+ case '_': do_scan(BX_KEY_MINUS,1,0,alt); break;
+
+ // `abcdefg
+ case '`': do_scan(BX_KEY_GRAVE,0,0,alt); break;
+ case 'a': do_scan(BX_KEY_A,0,0,alt); break;
+ case 'b': do_scan(BX_KEY_B,0,0,alt); break;
+ case 'c': do_scan(BX_KEY_C,0,0,alt); break;
+ case 'd': do_scan(BX_KEY_D,0,0,alt); break;
+ case 'e': do_scan(BX_KEY_E,0,0,alt); break;
+ case 'f': do_scan(BX_KEY_F,0,0,alt); break;
+ case 'g': do_scan(BX_KEY_G,0,0,alt); break;
+
+ // hijklmno
+ case 'h': do_scan(BX_KEY_H,0,0,alt); break;
+ case 'i': do_scan(BX_KEY_I,0,0,alt); break;
+ case 'j': do_scan(BX_KEY_J,0,0,alt); break;
+ case 'k': do_scan(BX_KEY_K,0,0,alt); break;
+ case 'l': do_scan(BX_KEY_L,0,0,alt); break;
+ case 'm': do_scan(BX_KEY_M,0,0,alt); break;
+ case 'n': do_scan(BX_KEY_N,0,0,alt); break;
+ case 'o': do_scan(BX_KEY_O,0,0,alt); break;
+
+ // pqrstuvw
+ case 'p': do_scan(BX_KEY_P,0,0,alt); break;
+ case 'q': do_scan(BX_KEY_Q,0,0,alt); break;
+ case 'r': do_scan(BX_KEY_R,0,0,alt); break;
+ case 's': do_scan(BX_KEY_S,0,0,alt); break;
+ case 't': do_scan(BX_KEY_T,0,0,alt); break;
+ case 'u': do_scan(BX_KEY_U,0,0,alt); break;
+ case 'v': do_scan(BX_KEY_V,0,0,alt); break;
+ case 'w': do_scan(BX_KEY_W,0,0,alt); break;
+
+ // xyz{|}~
+ case 'x': do_scan(BX_KEY_X,0,0,alt); break;
+ case 'y': do_scan(BX_KEY_Y,0,0,alt); break;
+ case 'z': do_scan(BX_KEY_Z,0,0,alt); break;
+ case '[': do_scan(BX_KEY_LEFT_BRACKET,0,0,alt); break;
+ case '\\': do_scan(BX_KEY_BACKSLASH,0,0,alt); break;
+ case ']': do_scan(BX_KEY_RIGHT_BRACKET,0,0,alt); break;
+ case '~': do_scan(BX_KEY_GRAVE,1,0,alt); break;
+
+ // function keys
+ case KEY_F(1): do_scan(BX_KEY_F1,0,0,alt); break;
+ case KEY_F(2): do_scan(BX_KEY_F2,0,0,alt); break;
+ case KEY_F(3): do_scan(BX_KEY_F3,0,0,alt); break;
+ case KEY_F(4): do_scan(BX_KEY_F4,0,0,alt); break;
+ case KEY_F(5): do_scan(BX_KEY_F5,0,0,alt); break;
+ case KEY_F(6): do_scan(BX_KEY_F6,0,0,alt); break;
+ case KEY_F(7): do_scan(BX_KEY_F7,0,0,alt); break;
+ case KEY_F(8): do_scan(BX_KEY_F8,0,0,alt); break;
+ case KEY_F(9): do_scan(BX_KEY_F9,0,0,alt); break;
+ case KEY_F(10): do_scan(BX_KEY_F10,0,0,alt); break;
+ case KEY_F(11): do_scan(BX_KEY_F11,0,0,alt); break;
+ case KEY_F(12): do_scan(BX_KEY_F12,0,0,alt); break;
+
+ // shifted function keys
+ case KEY_F(13): do_scan(BX_KEY_F1,1,0,alt); break;
+ case KEY_F(14): do_scan(BX_KEY_F2,1,0,alt); break;
+ case KEY_F(15): do_scan(BX_KEY_F3,1,0,alt); break;
+ case KEY_F(16): do_scan(BX_KEY_F4,1,0,alt); break;
+ case KEY_F(17): do_scan(BX_KEY_F5,1,0,alt); break;
+ case KEY_F(18): do_scan(BX_KEY_F6,1,0,alt); break;
+ case KEY_F(19): do_scan(BX_KEY_F7,1,0,alt); break;
+ case KEY_F(20): do_scan(BX_KEY_F8,1,0,alt); break;
+
+ default:
+ if(character > 0x79) {
+ do_char(character - 0x80,1);
+ break;
+ }
+
+ BX_INFO(("character unhandled: 0x%x",character));
+ break;
+ }
+}
+
+
+// ::HANDLE_EVENTS()
+//
+// Called periodically (vga_update_interval in .bochsrc) so the
+// the gui code can poll for keyboard, mouse, and other
+// relevant events.
+
+ void
+bx_term_gui_c::handle_events(void)
+{
+ int character;
+ while((character = getch()) != ERR) {
+ BX_DEBUG(("scancode(0x%x)",character));
+ do_char(character,0);
+ }
+}
+
+
+
+// ::FLUSH()
+//
+// Called periodically, requesting that the gui code flush all pending
+// screen update requests.
+
+ void
+bx_term_gui_c::flush(void)
+{
+ if (initialized)
+ refresh();
+}
+
+
+// ::CLEAR_SCREEN()
+//
+// Called to request that the VGA region is cleared. Don't
+// clear the area that defines the headerbar.
+
+ void
+bx_term_gui_c::clear_screen(void)
+{
+ clear();
+#if BX_HAVE_COLOR_SET
+ color_set(7, NULL);
+#endif
+#if BX_HAVE_MVHLINE
+ if (LINES > (int)text_rows) {
+ mvhline(text_rows, 0, ACS_HLINE, text_cols);
+ }
+#endif
+#if BX_HAVE_MVVLINE
+ if (COLS > (int)text_cols) {
+ mvvline(0, text_cols, ACS_VLINE, text_rows);
+ }
+#endif
+ if ((LINES > (int)text_rows) && (COLS > (int)text_cols)) {
+ mvaddch(text_rows, text_cols, ACS_LRCORNER);
+ }
+}
+
+int
+get_color_pair(Bit8u vga_attr)
+{
+ int term_attr;
+
+ term_attr = curses_color[vga_attr & 0x07];
+ term_attr |= (curses_color[(vga_attr & 0x70) >> 4] << 3);
+ return term_attr;
+}
+
+chtype
+get_term_char(Bit8u vga_char[])
+{
+ int term_char;
+
+ if ((vga_char[1] & 0x0f) == ((vga_char[1] >> 4) & 0x0f)) {
+ return ' ';
+ }
+ switch (vga_char[0]) {
+ case 0x04: term_char = ACS_DIAMOND; break;
+ case 0x18: term_char = ACS_UARROW; break;
+ case 0x19: term_char = ACS_DARROW; break;
+ case 0x1a: term_char = ACS_RARROW; break;
+ case 0x1b: term_char = ACS_LARROW; break;
+ case 0xc4:
+ case 0xcd: term_char = ACS_HLINE; break;
+ case 0xb3:
+ case 0xba: term_char = ACS_VLINE; break;
+ case 0xc9:
+ case 0xd5:
+ case 0xd6:
+ case 0xda: term_char = ACS_ULCORNER; break;
+ case 0xb7:
+ case 0xb8:
+ case 0xbb:
+ case 0xbf: term_char = ACS_URCORNER; break;
+ case 0xc0:
+ case 0xc8:
+ case 0xd3:
+ case 0xd4: term_char = ACS_LLCORNER; break;
+ case 0xbc:
+ case 0xbd:
+ case 0xbe:
+ case 0xd9: term_char = ACS_LRCORNER; break;
+ case 0xc3:
+ case 0xc6:
+ case 0xc7:
+ case 0xcc: term_char = ACS_LTEE; break;
+ case 0xb4:
+ case 0xb5:
+ case 0xb6:
+ case 0xb9: term_char = ACS_RTEE; break;
+ case 0xc2:
+ case 0xcb:
+ case 0xd1:
+ case 0xd2: term_char = ACS_TTEE; break;
+ case 0xc1:
+ case 0xca:
+ case 0xcf:
+ case 0xd0: term_char = ACS_BTEE; break;
+ case 0xc5:
+ case 0xce:
+ case 0xd7:
+ case 0xd8: term_char = ACS_PLUS; break;
+ case 0xb0:
+ case 0xb1: term_char = ACS_CKBOARD; break;
+ case 0xb2: term_char = ACS_BOARD; break;
+ case 0xdb: term_char = ACS_BLOCK; break;
+ default:
+ if (vga_char[0] > 0x7f) {
+ term_char = vga_to_term[vga_char[0]-0x80];
+ } else if (vga_char[0] > 0x1f) {
+ term_char = vga_char[0];
+ } else {
+ term_char = ' ';
+ }
+ }
+ return term_char;
+}
+
+// ::TEXT_UPDATE()
+//
+// Called in a VGA text mode, to update the screen with
+// new content.
+//
+// old_text: array of character/attributes making up the contents
+// of the screen from the last call. See below
+// new_text: array of character/attributes making up the current
+// contents, which should now be displayed. See below
+//
+// format of old_text & new_text: each is 4000 bytes long.
+// This represents 80 characters wide by 25 high, with
+// each character being 2 bytes. The first by is the
+// character value, the second is the attribute byte.
+// I currently don't handle the attribute byte.
+//
+// cursor_x: new x location of cursor
+// cursor_y: new y location of cursor
+
+ void
+bx_term_gui_c::text_update(Bit8u *old_text, Bit8u *new_text,
+ unsigned long cursor_x, unsigned long cursor_y,
+ bx_vga_tminfo_t tm_info, unsigned nrows)
+{
+ unsigned char *old_line, *new_line, *new_start;
+ unsigned char cAttr;
+ unsigned int hchars, rows, x, y;
+ chtype ch;
+ bx_bool force_update = 0;
+
+ UNUSED(nrows);
+
+ if(charmap_updated) {
+ force_update = 1;
+ charmap_updated = 0;
+ }
+
+ new_start = new_text;
+ rows = text_rows;
+ y = 0;
+ do {
+ hchars = text_cols;
+ new_line = new_text;
+ old_line = old_text;
+ x = 0;
+ do {
+ if (force_update || (old_text[0] != new_text[0])
+ || (old_text[1] != new_text[1])) {
+#if BX_HAVE_COLOR_SET
+ if (has_colors()) {
+ color_set(get_color_pair(new_text[1]), NULL);
+ }
+#endif
+ ch = get_term_char(&new_text[0]);
+ if ((new_text[1] & 0x08) > 0) ch |= A_BOLD;
+ if ((new_text[1] & 0x80) > 0) ch |= A_BLINK;
+ mvaddch(y, x, ch);
+ }
+ x++;
+ new_text+=2;
+ old_text+=2;
+ } while (--hchars);
+ y++;
+ new_text = new_line + tm_info.line_offset;
+ old_text = old_line + tm_info.line_offset;
+ } while (--rows);
+
+ if ((cursor_x<text_cols) && (cursor_y<text_rows)
+ && (tm_info.cs_start <= tm_info.cs_end)) {
+ if(cursor_x>0)
+ cursor_x--;
+ else {
+ cursor_x=COLS-1;
+ cursor_y--;
+ }
+ cAttr = new_start[cursor_y*tm_info.line_offset+cursor_x*2+1];
+#if BX_HAVE_COLOR_SET
+ if (has_colors()) {
+ color_set(get_color_pair(cAttr), NULL);
+ }
+#endif
+ ch = get_term_char(&new_start[cursor_y*tm_info.line_offset+cursor_x*2]);
+ if ((cAttr & 0x08) > 0) ch |= A_BOLD;
+ if ((cAttr & 0x80) > 0) ch |= A_REVERSE;
+ mvaddch(cursor_y, cursor_x, ch);
+ curs_set(2);
+ } else {
+ curs_set(0);
+ }
+}
+
+ int
+bx_term_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes)
+{
+ return 0;
+}
+
+ int
+bx_term_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len)
+{
+ return 0;
+}
+
+
+// ::PALETTE_CHANGE()
+//
+// Allocate a color in the native GUI, for this color, and put
+// it in the colormap location 'index'.
+// returns: 0=no screen update needed (color map change has direct effect)
+// 1=screen updated needed (redraw using current colormap)
+
+ bx_bool
+bx_term_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue)
+{
+ BX_DEBUG(("color pallete request (%d,%d,%d,%d) ignored",
+ index,red,green,blue));
+ return(0);
+}
+
+
+// ::GRAPHICS_TILE_UPDATE()
+//
+// Called to request that a tile of graphics be drawn to the
+// screen, since info in this region has changed.
+//
+// tile: array of 8bit values representing a block of pixels with
+// dimension equal to the 'tilewidth' & 'tileheight' parameters to
+// ::specific_init(). Each value specifies an index into the
+// array of colors you allocated for ::palette_change()
+// x0: x origin of tile
+// y0: y origin of tile
+//
+// note: origin of tile and of window based on (0,0) being in the upper
+// left of the window.
+
+ void
+bx_term_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
+{
+ UNUSED(tile);
+ UNUSED(x0);
+ UNUSED(y0);
+}
+
+
+
+// ::DIMENSION_UPDATE()
+//
+// Called when the VGA mode changes it's X,Y dimensions.
+// Resize the window to this size, but you need to add on
+// the height of the headerbar to the Y value.
+//
+// x: new VGA x size
+// y: new VGA y size (add headerbar_y parameter from ::specific_init().
+// fheight: new VGA character height in text mode
+// fwidth : new VGA character width in text mode
+// bpp : bits per pixel in graphics mode
+
+ void
+bx_term_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp)
+{
+ if (bpp > 8) {
+ BX_PANIC(("%d bpp graphics mode not supported", bpp));
+ }
+ if (fheight > 0) {
+ text_cols = x / fwidth;
+ text_rows = y / fheight;
+#if BX_HAVE_COLOR_SET
+ color_set(7, NULL);
+#endif
+#if BX_HAVE_MVHLINE
+ if (LINES > (int)text_rows) {
+ mvhline(text_rows, 0, ACS_HLINE, text_cols);
+ }
+#endif
+#if BX_HAVE_MVVLINE
+ if (COLS > (int)text_cols) {
+ mvvline(0, text_cols, ACS_VLINE, text_rows);
+ }
+#endif
+ if ((LINES > (int)text_rows) && (COLS > (int)text_cols)) {
+ mvaddch(text_rows, text_cols, ACS_LRCORNER);
+ }
+ }
+}
+
+
+// ::CREATE_BITMAP()
+//
+// Create a monochrome bitmap of size 'xdim' by 'ydim', which will
+// be drawn in the headerbar. Return an integer ID to the bitmap,
+// with which the bitmap can be referenced later.
+//
+// bmap: packed 8 pixels-per-byte bitmap. The pixel order is:
+// bit0 is the left most pixel, bit7 is the right most pixel.
+// xdim: x dimension of bitmap
+// ydim: y dimension of bitmap
+
+ unsigned
+bx_term_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim)
+{
+ UNUSED(bmap);
+ UNUSED(xdim);
+ UNUSED(ydim);
+ return(0);
+}
+
+
+// ::HEADERBAR_BITMAP()
+//
+// Called to install a bitmap in the bochs headerbar (toolbar).
+//
+// bmap_id: will correspond to an ID returned from
+// ::create_bitmap(). 'alignment' is either BX_GRAVITY_LEFT
+// or BX_GRAVITY_RIGHT, meaning install the bitmap in the next
+// available leftmost or rightmost space.
+// alignment: is either BX_GRAVITY_LEFT or BX_GRAVITY_RIGHT,
+// meaning install the bitmap in the next
+// available leftmost or rightmost space.
+// f: a 'C' function pointer to callback when the mouse is clicked in
+// the boundaries of this bitmap.
+
+ unsigned
+bx_term_gui_c::headerbar_bitmap(unsigned bmap_id, unsigned alignment, void (*f)(void))
+{
+ UNUSED(bmap_id);
+ UNUSED(alignment);
+ UNUSED(f);
+ return(0);
+}
+
+
+// ::SHOW_HEADERBAR()
+//
+// Show (redraw) the current headerbar, which is composed of
+// currently installed bitmaps.
+
+ void
+bx_term_gui_c::show_headerbar(void)
+{
+}
+
+
+// ::REPLACE_BITMAP()
+//
+// Replace the bitmap installed in the headerbar ID slot 'hbar_id',
+// with the one specified by 'bmap_id'. 'bmap_id' will have
+// been generated by ::create_bitmap(). The old and new bitmap
+// must be of the same size. This allows the bitmap the user
+// sees to change, when some action occurs. For example when
+// the user presses on the floppy icon, it then displays
+// the ejected status.
+//
+// hbar_id: headerbar slot ID
+// bmap_id: bitmap ID
+
+ void
+bx_term_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id)
+{
+ UNUSED(hbar_id);
+ UNUSED(bmap_id);
+}
+
+
+// ::EXIT()
+//
+// Called before bochs terminates, to allow for a graceful
+// exit from the native GUI mechanism.
+
+ void
+bx_term_gui_c::exit(void)
+{
+ if (!initialized) return;
+ clear();
+ flush();
+ endwin();
+ BX_DEBUG(("exiting"));
+}
+
+ void
+bx_term_gui_c::mouse_enabled_changed_specific (bx_bool val)
+{
+}
+#endif /* if BX_WITH_TERM */
diff --git a/tools/ioemu/gui/textconfig.cc b/tools/ioemu/gui/textconfig.cc
new file mode 100644
index 0000000000..7b5b098453
--- /dev/null
+++ b/tools/ioemu/gui/textconfig.cc
@@ -0,0 +1,995 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: textconfig.cc,v 1.18 2003/10/24 15:39:57 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// This is code for a text-mode configuration interface. Note that this file
+// does NOT include bochs.h. Instead, it does all of its contact with
+// the simulator through an object called SIM, defined in siminterface.cc
+// and siminterface.h. This separation adds an extra layer of method
+// calls before any work can be done, but the benefit is that the compiler
+// enforces the rules. I can guarantee that textconfig.cc doesn't call any
+// I/O device objects directly, for example, because the bx_devices symbol
+// isn't even defined in this context.
+//
+
+#include "config.h"
+
+extern "C" {
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+}
+#include "osdep.h"
+#include "textconfig.h"
+#include "siminterface.h"
+#include "extplugin.h"
+#ifdef WIN32
+#include "win32dialog.h"
+#endif
+
+#define CI_PATH_LENGTH 512
+
+#define BX_INSERTED 11
+
+/* functions for changing particular options */
+void bx_config_interface_init ();
+int bx_read_rc (char *rc);
+int bx_write_rc (char *rc);
+void bx_log_options (int individual);
+
+/******************************************************************/
+/* lots of code stolen from bximage.c */
+/* remove leading spaces, newline junk at end. returns pointer to
+ cleaned string, which is between s0 and the null */
+char *
+clean_string (char *s0)
+{
+ char *s = s0;
+ char *ptr;
+ /* find first nonblank */
+ while (isspace (*s))
+ s++;
+ /* truncate string at first non-alphanumeric */
+ ptr = s;
+ while (isprint (*ptr))
+ ptr++;
+ *ptr = 0;
+ return s;
+}
+
+void
+double_percent (char *s, int max_len)
+{
+ char d[CI_PATH_LENGTH];
+ int i=0,j=0;
+
+ if (max_len>CI_PATH_LENGTH)
+ max_len=CI_PATH_LENGTH;
+
+ max_len--;
+
+ while((s[i]!=0)&&(j<max_len))
+ {
+ d[j++]=s[i];
+ if((s[i]=='%')&&(j<max_len))
+ {
+ d[j++]=s[i];
+ }
+ i++;
+ }
+ d[j]=0;
+ strcpy(s,d);
+}
+
+/* returns 0 on success, -1 on failure. The value goes into out. */
+int
+ask_uint (char *prompt, Bit32u min, Bit32u max, Bit32u the_default, Bit32u *out, int base)
+{
+ Bit32u n = max + 1;
+ char buffer[1024];
+ char *clean;
+ int illegal;
+ assert (base==10 || base==16);
+ while (1) {
+ printf (prompt, the_default);
+ if (!fgets (buffer, sizeof(buffer), stdin))
+ return -1;
+ clean = clean_string (buffer);
+ if (strlen(clean) < 1) {
+ // empty line, use the default
+ *out = the_default;
+ return 0;
+ }
+ const char *format = (base==10) ? "%d" : "%x";
+ illegal = (1 != sscanf (buffer, format, &n));
+ if (illegal || n<min || n>max) {
+ printf ("Your choice (%s) was not an integer between %u and %u.\n\n",
+ clean, min, max);
+ } else {
+ // choice is okay
+ *out = n;
+ return 0;
+ }
+ }
+}
+
+// identical to ask_uint, but uses signed comparisons
+int
+ask_int (char *prompt, Bit32s min, Bit32s max, Bit32s the_default, Bit32s *out)
+{
+ int n = max + 1;
+ char buffer[1024];
+ char *clean;
+ int illegal;
+ while (1) {
+ printf (prompt, the_default);
+ if (!fgets (buffer, sizeof(buffer), stdin))
+ return -1;
+ clean = clean_string (buffer);
+ if (strlen(clean) < 1) {
+ // empty line, use the default
+ *out = the_default;
+ return 0;
+ }
+ illegal = (1 != sscanf (buffer, "%d", &n));
+ if (illegal || n<min || n>max) {
+ printf ("Your choice (%s) was not an integer between %d and %d.\n\n",
+ clean, min, max);
+ } else {
+ // choice is okay
+ *out = n;
+ return 0;
+ }
+ }
+}
+
+int
+ask_menu (char *prompt, int n_choices, char *choice[], int the_default, int *out)
+{
+ char buffer[1024];
+ char *clean;
+ int i;
+ *out = -1;
+ while (1) {
+ printf (prompt, choice[the_default]);
+ if (!fgets (buffer, sizeof(buffer), stdin))
+ return -1;
+ clean = clean_string (buffer);
+ if (strlen(clean) < 1) {
+ // empty line, use the default
+ *out = the_default;
+ return 0;
+ }
+ for (i=0; i<n_choices; i++) {
+ if (!strcmp (choice[i], clean)) {
+ // matched, return the choice number
+ *out = i;
+ return 0;
+ }
+ }
+ if (clean[0] != '?')
+ printf ("Your choice (%s) did not match any of the choices:\n", clean);
+ for (i=0; i<n_choices; i++) {
+ if (i>0) printf (", ");
+ printf ("%s", choice[i]);
+ }
+ printf ("\n");
+ }
+}
+
+int
+ask_yn (char *prompt, Bit32u the_default, Bit32u *out)
+{
+ char buffer[16];
+ char *clean;
+ *out = 1<<31;
+ while (1) {
+ // if there's a %s field, substitute in the default yes/no.
+ printf (prompt, the_default ? "yes" : "no");
+ if (!fgets (buffer, sizeof(buffer), stdin))
+ return -1;
+ clean = clean_string (buffer);
+ if (strlen(clean) < 1) {
+ // empty line, use the default
+ *out = the_default;
+ return 0;
+ }
+ switch (tolower(clean[0])) {
+ case 'y': *out=1; return 0;
+ case 'n': *out=0; return 0;
+ }
+ printf ("Please type either yes or no.\n");
+ }
+}
+
+// returns -1 on error (stream closed or something)
+// returns 0 if default was taken
+// returns 1 if value changed
+int
+ask_string (char *prompt, char *the_default, char *out)
+{
+ char buffer[1024];
+ char *clean;
+ assert (the_default != out);
+ out[0] = 0;
+ printf (prompt, the_default);
+ if (fgets (buffer, sizeof(buffer), stdin) == NULL)
+ return -1;
+ clean = clean_string (buffer);
+ if (strlen(clean) < 1) {
+ // empty line, use the default
+ strcpy (out, the_default);
+ return 0;
+ }
+ strcpy (out, clean);
+ return 1;
+}
+
+/******************************************************************/
+
+static char *startup_menu_prompt =
+"------------------------------\n"
+"Bochs Configuration: Main Menu\n"
+"------------------------------\n"
+"\n"
+"This is the Bochs Configuration Interface, where you can describe the\n"
+"machine that you want to simulate. Bochs has already searched for a\n"
+"configuration file (typically called bochsrc.txt) and loaded it if it\n"
+"could be found. When you are satisfied with the configuration, go\n"
+"ahead and start the simulation.\n"
+"\n"
+"You can also start bochs with the -q option to skip these menus.\n"
+"\n"
+"1. Restore factory default configuration\n"
+"2. Read options from...\n"
+"3. Edit options\n"
+"4. Save options to...\n"
+"5. Begin simulation\n"
+"6. Quit now\n"
+"\n"
+"Please choose one: [%d] ";
+
+static char *startup_options_prompt =
+"------------------\n"
+"Bochs Options Menu\n"
+"------------------\n"
+"0. Return to previous menu\n"
+"1. Log file: %s\n"
+"2. Log prefix: %s\n"
+"3. Debug log file: %s\n"
+"4. Log options for all devices\n"
+"5. Log options for individual devices\n"
+"6. Memory options\n"
+"7. Interface options\n"
+"8. Disk options\n"
+"9. Serial or Parallel port options\n"
+"10. Sound Blaster 16 options\n"
+"11. NE2000 network card options\n"
+"12. Keyboard options\n"
+"13. Other options\n"
+"\n"
+"Please choose one: [0] ";
+
+static char *runtime_menu_prompt =
+"---------------------\n"
+"Bochs Runtime Options\n"
+"---------------------\n"
+"1. Floppy disk 0: %s\n"
+"2. Floppy disk 1: %s\n"
+"3. 1st CDROM: %s\n"
+"4. 2nd CDROM: %s\n"
+"5. 3rd CDROM: %s\n"
+"6. 4th CDROM: %s\n"
+"7. (not implemented)\n"
+"8. Log options for all devices\n"
+"9. Log options for individual devices\n"
+"10. VGA Update Interval: %d\n"
+"11. Mouse: %s\n"
+"12. Keyboard paste delay: %d\n"
+"13. Userbutton shortcut: %s\n"
+"14. Instruction tracing: off (doesn't exist yet)\n"
+"15. Continue simulation\n"
+"16. Quit now\n"
+"\n"
+"Please choose one: [15] ";
+
+#define NOT_IMPLEMENTED(choice) \
+ fprintf (stderr, "ERROR: choice %d not implemented\n", choice);
+
+#define BAD_OPTION(menu,choice) \
+ do {fprintf (stderr, "ERROR: menu %d has no choice %d\n", menu, choice); \
+ assert (0); } while (0)
+
+void build_runtime_options_prompt (char *format, char *buf, int size)
+{
+ bx_floppy_options floppyop;
+ bx_atadevice_options cdromop;
+/* bx_param_num_c *ips = SIM->get_param_num (BXP_IPS); */
+ char buffer[6][128];
+ for (int i=0; i<2; i++) {
+ SIM->get_floppy_options (i, &floppyop);
+ if (floppyop.Odevtype->get () == BX_FLOPPY_NONE)
+ strcpy (buffer[i], "(not present)");
+ else {
+ sprintf (buffer[i], "%s, size=%s, %s", floppyop.Opath->getptr (),
+ SIM->get_floppy_type_name (floppyop.Otype->get ()),
+ (floppyop.Ostatus->get () == BX_INSERTED)? "inserted" : "ejected");
+ if (!floppyop.Opath->getptr ()[0]) strcpy (buffer[i], "none");
+ }
+ }
+
+ // 4 cdroms supported at run time
+ int device;
+ for (Bit8u cdrom=0; cdrom<4; cdrom++) {
+ if (!SIM->get_cdrom_options (cdrom, &cdromop, &device) || !cdromop.Opresent->get ())
+ sprintf (buffer[2+cdrom], "(not present)");
+ else
+ sprintf (buffer[2+cdrom], "(%s on ata%d) %s, %s",
+ device&1?"slave":"master", device/2, cdromop.Opath->getptr (),
+ (cdromop.Ostatus->get () == BX_INSERTED)? "inserted" : "ejected");
+ }
+
+ snprintf (buf, size, format, buffer[0], buffer[1], buffer[2],
+ buffer[3], buffer[4], buffer[5],
+ /* ips->get (), */
+ SIM->get_param_num (BXP_VGA_UPDATE_INTERVAL)->get (),
+ SIM->get_param_num (BXP_MOUSE_ENABLED)->get () ? "enabled" : "disabled",
+ SIM->get_param_num (BXP_KBD_PASTE_DELAY)->get (),
+ SIM->get_param_string (BXP_USER_SHORTCUT)->getptr ());
+}
+
+int do_menu (bx_id id) {
+ bx_list_c *menu = (bx_list_c *)SIM->get_param (id);
+ while (1) {
+ menu->get_choice()->set (0);
+ int status = menu->text_ask (stdin, stderr);
+ if (status < 0) return status;
+ bx_param_num_c *choice = menu->get_choice();
+ if (choice->get () < 1)
+ return choice->get ();
+ else {
+ int index = choice->get () - 1; // choosing 1 means list[0]
+ bx_param_c *chosen = menu->get (index);
+ assert (chosen != NULL);
+ chosen->text_ask (stdin, stderr);
+ }
+ }
+}
+
+void askparam (bx_id id)
+{
+ bx_param_c *param = SIM->get_param (id);
+ param->text_ask (stdin, stderr);
+}
+
+int bx_config_interface (int menu)
+{
+ Bit32u choice;
+ while (1) {
+ switch (menu)
+ {
+ case BX_CI_INIT:
+ bx_config_interface_init ();
+ return 0;
+ case BX_CI_START_SIMULATION: {
+ SIM->begin_simulation (bx_startup_flags.argc, bx_startup_flags.argv);
+ // we don't expect it to return, but if it does, quit
+ SIM->quit_sim(1);
+ break;
+ }
+ case BX_CI_START_MENU:
+ {
+ Bit32u default_choice;
+ switch (SIM->get_param_enum(BXP_BOCHS_START)->get ()) {
+ case BX_LOAD_START:
+ default_choice = 2; break;
+ case BX_EDIT_START:
+ default_choice = 3; break;
+ default:
+ default_choice = 5; break;
+ }
+
+ if (ask_uint (startup_menu_prompt, 1, 6, default_choice, &choice, 10) < 0) return -1;
+ switch (choice) {
+ case 1:
+ fprintf (stderr, "I reset all options back to their factory defaults.\n\n");
+ SIM->reset_all_param ();
+ SIM->get_param_enum(BXP_BOCHS_START)->set(BX_EDIT_START);
+ break;
+ case 2:
+ // Before reading a new configuration, reset every option to its
+ // original state.
+ SIM->reset_all_param ();
+ if (bx_read_rc (NULL) >= 0)
+ SIM->get_param_enum(BXP_BOCHS_START)->set(BX_RUN_START);
+ break;
+ case 3:
+ bx_config_interface (BX_CI_START_OPTS);
+ SIM->get_param_enum(BXP_BOCHS_START)->set(BX_RUN_START);
+ break;
+ case 4: bx_write_rc (NULL); break;
+ case 5: bx_config_interface (BX_CI_START_SIMULATION); break;
+ case 6: SIM->quit_sim (1); return -1;
+ default: BAD_OPTION(menu, choice);
+ }
+ }
+ break;
+ case BX_CI_START_OPTS:
+ {
+ char prompt[CI_PATH_LENGTH];
+ char oldpath[CI_PATH_LENGTH];
+ char olddebuggerpath[CI_PATH_LENGTH];
+ char oldprefix[CI_PATH_LENGTH];
+ int retval;
+
+ retval = SIM->get_log_file (oldpath, CI_PATH_LENGTH);
+ assert (retval >= 0);
+ double_percent(oldpath,CI_PATH_LENGTH);
+ retval = SIM->get_log_prefix (oldprefix, CI_PATH_LENGTH);
+ assert (retval >= 0);
+ double_percent(oldprefix,CI_PATH_LENGTH);
+ retval = SIM->get_debugger_log_file (olddebuggerpath, CI_PATH_LENGTH);
+ assert (retval >= 0);
+ double_percent(olddebuggerpath,CI_PATH_LENGTH);
+
+ sprintf (prompt, startup_options_prompt, oldpath, oldprefix, olddebuggerpath);
+ if (ask_uint (prompt, 0, 13, 0, &choice, 10) < 0) return -1;
+ switch (choice) {
+ case 0: return 0;
+ case 1: askparam (BXP_LOG_FILENAME); break;
+ case 2: askparam (BXP_LOG_PREFIX); break;
+ case 3: askparam (BXP_DEBUGGER_LOG_FILENAME); break;
+ case 4: bx_log_options (0); break;
+ case 5: bx_log_options (1); break;
+ case 6: do_menu (BXP_MENU_MEMORY); break;
+ case 7: do_menu (BXP_MENU_INTERFACE); break;
+ case 8: do_menu (BXP_MENU_DISK); break;
+ case 9: do_menu (BXP_MENU_SERIAL_PARALLEL); break;
+ case 10: do_menu (BXP_SB16); break;
+ case 11: do_menu (BXP_NE2K); break;
+ case 12: do_menu (BXP_MENU_KEYBOARD); break;
+ case 13: do_menu (BXP_MENU_MISC); break;
+ default: BAD_OPTION(menu, choice);
+ }
+ }
+ break;
+ case BX_CI_RUNTIME:
+ char prompt[1024];
+ bx_floppy_options floppyop;
+ bx_atadevice_options cdromop;
+ build_runtime_options_prompt (runtime_menu_prompt, prompt, 1024);
+ if (ask_uint (prompt, 1, 16, 15, &choice, 10) < 0) return -1;
+ switch (choice) {
+ case 1:
+ SIM->get_floppy_options (0, &floppyop);
+ if (floppyop.Odevtype->get () != BX_FLOPPY_NONE) do_menu (BXP_FLOPPYA);
+ break;
+ case 2:
+ SIM->get_floppy_options (1, &floppyop);
+ if (floppyop.Odevtype->get () != BX_FLOPPY_NONE) do_menu (BXP_FLOPPYB);
+ break;
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ int device;
+ if (SIM->get_cdrom_options (choice - 3, &cdromop, &device) && cdromop.Opresent->get ()) {
+ // disable type selection
+ SIM->get_param((bx_id)(BXP_ATA0_MASTER_TYPE + device))->set_enabled(0);
+ SIM->get_param((bx_id)(BXP_ATA0_MASTER_MODEL + device))->set_enabled(0);
+ SIM->get_param((bx_id)(BXP_ATA0_MASTER_BIOSDETECT + device))->set_enabled(0);
+ do_menu ((bx_id)(BXP_ATA0_MASTER + device));
+ }
+ break;
+ case 7: // not implemented yet because I would have to mess with
+ // resetting timers and pits and everything on the fly.
+ // askparam (BXP_IPS);
+ break;
+ case 8: bx_log_options (0); break;
+ case 9: bx_log_options (1); break;
+ case 10: askparam (BXP_VGA_UPDATE_INTERVAL); break;
+ case 11: askparam (BXP_MOUSE_ENABLED); break;
+ case 12: askparam (BXP_KBD_PASTE_DELAY); break;
+ case 13: askparam (BXP_USER_SHORTCUT); break;
+ case 14: NOT_IMPLEMENTED (choice); break;
+ case 15: fprintf (stderr, "Continuing simulation\n"); return 0;
+ case 16:
+ fprintf (stderr, "You chose quit on the configuration interface.\n");
+ SIM->quit_sim (1);
+ return -1;
+ default: fprintf (stderr, "Menu choice %d not implemented.\n", choice);
+ }
+ break;
+ default:
+ fprintf (stderr, "Unknown config interface menu type.\n");
+ assert (menu >=0 && menu < BX_CI_N_MENUS);
+ }
+ }
+}
+
+static void bx_print_log_action_table ()
+{
+ // just try to print all the prefixes first.
+ fprintf (stderr, "Current log settings:\n");
+ fprintf (stderr, " Debug Info Error Panic Pass\n");
+ fprintf (stderr, "ID Device Action Action Action Action Action\n");
+ fprintf (stderr, "---- --------- --------- --------- ---------- ---------- ----------\n");
+ int i, j, imax=SIM->get_n_log_modules ();
+ for (i=0; i<imax; i++) {
+ if (strcmp(SIM->get_prefix(i), "[ ]")) {
+ fprintf (stderr, "%3d. %s ", i, SIM->get_prefix (i));
+ for (j=0; j<SIM->get_max_log_level (); j++) {
+ fprintf (stderr, "%10s ", SIM->get_action_name (SIM->get_log_action (i, j)));
+ }
+ fprintf (stderr, "\n");
+ }
+ }
+}
+
+static char *log_options_prompt1 = "Enter the ID of the device to edit, or -1 to return: [-1] ";
+static char *log_level_choices[] = { "ignore", "report", "ask", "fatal", "no change" };
+static int log_level_n_choices_normal = 4;
+
+void bx_log_options (int individual)
+{
+ if (individual) {
+ int done = 0;
+ while (!done) {
+ bx_print_log_action_table ();
+ Bit32s id, level, action;
+ Bit32s maxid = SIM->get_n_log_modules ();
+ if (ask_int (log_options_prompt1, -1, maxid-1, -1, &id) < 0)
+ return;
+ if (id < 0) return;
+ fprintf (stderr, "Editing log options for the device %s\n", SIM->get_prefix (id));
+ for (level=0; level<SIM->get_max_log_level (); level++) {
+ char prompt[1024];
+ int default_action = SIM->get_log_action (id, level);
+ sprintf (prompt, "Enter action for %s event: [%s] ", SIM->get_log_level_name (level), SIM->get_action_name(default_action));
+ // don't show the no change choice (choices=3)
+ if (ask_menu (prompt, log_level_n_choices_normal, log_level_choices, default_action, &action)<0)
+ return;
+ SIM->set_log_action (id, level, action);
+ }
+ }
+ } else {
+ // provide an easy way to set log options for all devices at once
+ bx_print_log_action_table ();
+ for (int level=0; level<SIM->get_max_log_level (); level++) {
+ char prompt[1024];
+ int action, default_action = 3; // default to no change
+ sprintf (prompt, "Enter action for %s event on all devices: [no change] ", SIM->get_log_level_name (level));
+ // do show the no change choice (choices=4)
+ if (ask_menu (prompt, log_level_n_choices_normal+1, log_level_choices, default_action, &action)<0)
+ return;
+ if (action < 3) {
+ SIM->set_default_log_action (level, action);
+ SIM->set_log_action (-1, level, action);
+ }
+ }
+ }
+}
+
+int bx_read_rc (char *rc)
+{
+ if (rc && SIM->read_rc (rc) >= 0) return 0;
+ char oldrc[CI_PATH_LENGTH];
+ if (SIM->get_default_rc (oldrc, CI_PATH_LENGTH) < 0)
+ strcpy (oldrc, "none");
+ char newrc[CI_PATH_LENGTH];
+ while (1) {
+ if (ask_string ("\nWhat is the configuration file name?\nTo cancel, type 'none'. [%s] ", oldrc, newrc) < 0) return -1;
+ if (!strcmp (newrc, "none")) return -1;
+ if (SIM->read_rc (newrc) >= 0) return 0;
+ fprintf (stderr, "The file '%s' could not be found.\n", newrc);
+ }
+}
+
+int bx_write_rc (char *rc)
+{
+ char oldrc[CI_PATH_LENGTH], newrc[CI_PATH_LENGTH];
+ if (rc == NULL) {
+ if (SIM->get_default_rc (oldrc, CI_PATH_LENGTH) < 0)
+ strcpy (oldrc, "none");
+ } else {
+ strncpy (oldrc, rc, CI_PATH_LENGTH);
+ }
+ while (1) {
+ if (ask_string ("Save configuration to what file? To cancel, type 'none'.\n[%s] ", oldrc, newrc) < 0) return -1;
+ if (!strcmp (newrc, "none")) return 0;
+ // try with overwrite off first
+ int status = SIM->write_rc (newrc, 0);
+ if (status >= 0) {
+ fprintf (stderr, "Wrote configuration to '%s'.\n", newrc);
+ return 0;
+ } else if (status == -2) {
+ // return code -2 indicates the file already exists, and overwrite
+ // confirmation is required.
+ Bit32u overwrite = 0;
+ char prompt[256];
+ sprintf (prompt, "Configuration file '%s' already exists. Overwrite it? [no] ", newrc);
+ if (ask_yn (prompt, 0, &overwrite) < 0) return -1;
+ if (!overwrite) continue; // if "no", start loop over, asking for a different file
+ // they confirmed, so try again with overwrite bit set
+ if (SIM->write_rc (newrc, 1) >= 0) {
+ fprintf (stderr, "Overwriting existing configuration '%s'.\n", newrc);
+ return 0;
+ } else {
+ fprintf (stderr, "Write failed to '%s'.\n", newrc);
+ }
+ }
+ }
+}
+
+char *log_action_ask_choices[] = { "cont", "alwayscont", "die", "abort", "debug" };
+int log_action_n_choices = 4 + (BX_DEBUGGER?1:0);
+
+BxEvent *
+config_interface_notify_callback (void *unused, BxEvent *event)
+{
+#ifdef WIN32
+ int opts;
+ bx_param_c *param;
+ bx_param_string_c *sparam;
+#endif
+ event->retcode = -1;
+ switch (event->type)
+ {
+ case BX_SYNC_EVT_TICK:
+ event->retcode = 0;
+ return event;
+ case BX_SYNC_EVT_ASK_PARAM:
+#ifdef WIN32
+ param = event->u.param.param;
+ if (param->get_type() == BXT_PARAM_STRING) {
+ sparam = (bx_param_string_c *)param;
+ opts = sparam->get_options()->get();
+ if (opts & sparam->IS_FILENAME) {
+ if (param->get_id() == BXP_NULL) {
+ event->retcode = AskFilename(GetBochsWindow(), (bx_param_filename_c *)sparam);
+ } else {
+ event->retcode = FloppyDialog((bx_param_filename_c *)sparam);
+ }
+ return event;
+ } else {
+ event->retcode = AskString(sparam);
+ return event;
+ }
+ }
+#endif
+ event->u.param.param->text_ask (stdin, stderr);
+ return event;
+ case BX_SYNC_EVT_LOG_ASK:
+ {
+#ifdef WIN32
+ LogAskDialog(event);
+#else
+ int level = event->u.logmsg.level;
+ fprintf (stderr, "========================================================================\n");
+ fprintf (stderr, "Event type: %s\n", SIM->get_log_level_name (level));
+ fprintf (stderr, "Device: %s\n", event->u.logmsg.prefix);
+ fprintf (stderr, "Message: %s\n\n", event->u.logmsg.msg);
+ fprintf (stderr, "A %s has occurred. Do you want to:\n", SIM->get_log_level_name (level));
+ fprintf (stderr, " cont - continue execution\n");
+ fprintf (stderr, " alwayscont - continue execution, and don't ask again.\n");
+ fprintf (stderr, " This affects only %s events from device %s\n", SIM->get_log_level_name (level), event->u.logmsg.prefix);
+ fprintf (stderr, " die - stop execution now\n");
+ fprintf (stderr, " abort - dump core %s\n",
+ BX_HAVE_ABORT ? "" : "(Disabled)");
+#if BX_DEBUGGER
+ fprintf (stderr, " debug - continue and return to bochs debugger\n");
+#endif
+ int choice;
+ask:
+ if (ask_menu ("Choose one of the actions above: [%s] ",
+ log_action_n_choices, log_action_ask_choices, 2, &choice) < 0)
+ event->retcode = -1;
+ // return 0 for continue, 1 for alwayscontinue, 2 for die, 3 for debug.
+ if (!BX_HAVE_ABORT && choice==BX_LOG_ASK_CHOICE_DUMP_CORE) goto ask;
+ fflush(stdout);
+ fflush(stderr);
+ event->retcode = choice;
+#endif
+ }
+ return event;
+ case BX_ASYNC_EVT_REFRESH:
+ case BX_ASYNC_EVT_DBG_MSG:
+ // The text mode interface does not use these events, so just ignore
+ // them.
+ return event;
+ default:
+ fprintf (stderr, "Control panel: notify callback called with event type %04x\n", event->type);
+ return event;
+ }
+ assert (0); // switch statement should return
+}
+
+void bx_config_interface_init () {
+ //fprintf (stderr, "bx_config_interface_init()\n");
+ SIM->set_notify_callback (config_interface_notify_callback, NULL);
+}
+
+/////////////////////////////////////////////////////////////////////
+// implement the text_* methods for bx_param types.
+
+void
+bx_param_num_c::text_print (FILE *fp)
+{
+ //fprintf (fp, "number parameter, id=%u, name=%s\n", get_id (), get_name ());
+ //fprintf (fp, "value=%u\n", get ());
+ if (get_format ()) {
+ fprintf (fp, get_format (), get ());
+ } else {
+ char *format = "%s: %d";
+ assert (base==10 || base==16);
+ if (base==16) format = "%s: 0x%x";
+ fprintf (fp, format, get_name (), get ());
+ }
+}
+
+void
+bx_param_bool_c::text_print (FILE *fp)
+{
+ if (get_format ()) {
+ fprintf (fp, get_format (), get () ? "yes" : "no");
+ } else {
+ char *format = "%s: %s";
+ fprintf (fp, format, get_name (), get () ? "yes" : "no");
+ }
+}
+
+void
+bx_param_enum_c::text_print (FILE *fp)
+{
+ int n = get ();
+ assert (n >= min && n <= max);
+ char *choice = choices[n - min];
+ if (get_format ()) {
+ fprintf (fp, get_format (), choice);
+ } else {
+ char *format = "%s: %s";
+ fprintf (fp, format, get_name (), choice);
+ }
+}
+
+void
+bx_param_string_c::text_print (FILE *fp)
+{
+ char *value = getptr ();
+ int opts = options->get ();
+ if (opts & RAW_BYTES) {
+ char buffer[1024];
+ buffer[0] = 0;
+ char sep_string[2];
+ sep_string[0] = separator;
+ sep_string[1] = 0;
+ for (int i=0; i<maxsize; i++) {
+ char eachbyte[16];
+ sprintf (eachbyte, "%s%02x", (i>0)?sep_string : "", (unsigned int)0xff&val[i]);
+ strncat (buffer, eachbyte, sizeof(buffer));
+ }
+ if (strlen (buffer) > sizeof(buffer)-4) {
+ assert (0); // raw byte print buffer is probably overflowing. increase the max or make it dynamic
+ }
+ value = buffer;
+ }
+ if (get_format ()) {
+ fprintf (fp, get_format (), value);
+ } else {
+ fprintf (fp, "%s: %s", get_name (), value);
+ }
+}
+
+void
+bx_list_c::text_print (FILE *fp)
+{
+ //fprintf (fp, "This is a list.\n");
+ //fprintf (fp, "title=%s\n", title->getptr ());
+ fprintf (fp, "%s: ", get_name ());
+ /*
+ fprintf (fp, "options=%s%s%s\n",
+ (options->get () == 0) ? "none" : "",
+ (options->get () & SHOW_PARENT) ? "SHOW_PARENT " : "",
+ (options->get () & SERIES_ASK) ? "SERIES_ASK " : "");
+ */
+ for (int i=0; i<size; i++) {
+ //fprintf (fp, "param[%d] = %p\n", i, list[i]);
+ assert (list[i] != NULL);
+ if (list[i]->get_enabled ()) {
+ if ((i>0) && (options->get () & SERIES_ASK))
+ fprintf (fp, ", ");
+ list[i]->text_print (fp);
+ if (!(options->get () & SERIES_ASK))
+ fprintf (fp, "\n");
+ }
+ }
+}
+
+int
+bx_param_num_c::text_ask (FILE *fpin, FILE *fpout)
+{
+ fprintf (fpout, "\n");
+ int status;
+ char *prompt = get_ask_format ();
+ if (prompt == NULL) {
+ // default prompt, if they didn't set an ask format string
+ text_print (fpout);
+ fprintf (fpout, "\n");
+ prompt = "Enter new value: [%d] ";
+ if (base==16)
+ prompt = "Enter new value in hex: [%x] ";
+ }
+ Bit32u n = get ();
+ status = ask_uint (prompt, min, max, n, &n, base);
+ if (status < 0) return status;
+ set (n);
+ return 0;
+}
+
+int
+bx_param_bool_c::text_ask (FILE *fpin, FILE *fpout)
+{
+ fprintf (fpout, "\n");
+ int status;
+ char *prompt = get_ask_format ();
+ char buffer[512];
+ if (prompt == NULL) {
+ // default prompt, if they didn't set an ask format string
+ sprintf (buffer, "%s? [%%s] ", get_name ());
+ prompt = buffer;
+ }
+ Bit32u n = get ();
+ status = ask_yn (prompt, n, &n);
+ if (status < 0) return status;
+ set (n);
+ return 0;
+}
+
+int
+bx_param_enum_c::text_ask (FILE *fpin, FILE *fpout)
+{
+ fprintf (fpout, "\n");
+ char *prompt = get_ask_format ();
+ if (prompt == NULL) {
+ // default prompt, if they didn't set an ask format string
+ fprintf (fpout, "%s = ", get_name ());
+ text_print (fpout);
+ fprintf (fpout, "\n");
+ prompt = "Enter new value: [%s] ";
+ }
+ Bit32s n = (Bit32s)(get () - min);
+ int status = ask_menu (prompt, (max-min+1), choices, n, &n);
+ if (status < 0) return status;
+ n += (Bit32s)min;
+ set (n);
+ return 0;
+}
+
+int parse_raw_bytes (char *dest, char *src, int destsize, char separator)
+{
+ //printf ("parsing src='%s'\n", src);
+ int i;
+ unsigned int n;
+ for (i=0; i<destsize; i++)
+ dest[i] = 0;
+ for (i=0; i<destsize; i++) {
+ while (*src == separator)
+ src++;
+ if (*src == 0) break;
+ // try to read a byte of hex
+ if (sscanf (src, "%02x", &n) == 1) {
+ //printf ("found a byte %02x\n", n);
+ dest[i] = n;
+ src+=2;
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+bx_param_string_c::text_ask (FILE *fpin, FILE *fpout)
+{
+ fprintf (fpout, "\n");
+ int status;
+ char *prompt = get_ask_format ();
+ if (prompt == NULL) {
+ // default prompt, if they didn't set an ask format string
+ text_print (fpout);
+ fprintf (fpout, "\n");
+ prompt = "Enter a new value, or press return for no change.\n";
+ }
+ while (1) {
+ char buffer[1024];
+ status = ask_string (prompt, getptr(), buffer);
+ if (status < 0) return status;
+ int opts = options->get ();
+ char buffer2[1024];
+ strcpy (buffer2, buffer);
+ if (status == 1 && opts & RAW_BYTES) {
+ // copy raw hex into buffer
+ status = parse_raw_bytes (buffer, buffer2, maxsize, separator);
+ if (status < 0) {
+ fprintf (fpout, "Illegal raw byte format. I expected something like 3A%c03%c12%c...\n", separator, separator, separator);
+ continue;
+ }
+ }
+ if (!equals (buffer))
+ set (buffer);
+ return 0;
+ }
+}
+
+int
+bx_list_c::text_ask (FILE *fpin, FILE *fpout)
+{
+ char *my_title = title->getptr ();
+ fprintf (fpout, "\n");
+ int i, imax = strlen (my_title);
+ for (i=0; i<imax; i++) fprintf (fpout, "-");
+ fprintf (fpout, "\n%s\n", my_title);
+ for (i=0; i<imax; i++) fprintf (fpout, "-");
+ fprintf (fpout, "\n"); //fprintf (fp, "options=%s\n", options->get ());
+ if (options->get () & SERIES_ASK) {
+ for (int i=0; i<size; i++) {
+ if (list[i]->get_enabled ())
+ list[i]->text_ask (fpin, fpout);
+ }
+ } else {
+ if (options->get () & SHOW_PARENT)
+ fprintf (fpout, "0. Return to previous menu\n");
+ for (int i=0; i<size; i++) {
+ assert (list[i] != NULL);
+ fprintf (fpout, "%d. ", i+1);
+ if (list[i]->get_enabled ()) {
+ list[i]->text_print (fpout);
+ fprintf (fpout, "\n");
+ } else
+ fprintf (fpout, "(disabled)\n");
+ }
+ fprintf (fpout, "\n");
+ Bit32u n = choice->get ();
+ int min = (options->get () & SHOW_PARENT) ? 0 : 1;
+ int max = size;
+ int status = ask_uint ("Please choose one: [%d] ", min, max, n, &n, 10);
+ if (status < 0) return status;
+ choice->set (n);
+ }
+ return 0;
+}
+
+static int ci_callback (void *userdata, ci_command_t command)
+{
+ switch (command)
+ {
+ case CI_START:
+ //fprintf (stderr, "textconfig.cc: start\n");
+ bx_config_interface_init ();
+ if (SIM->get_param_enum(BXP_BOCHS_START)->get () == BX_QUICK_START)
+ bx_config_interface (BX_CI_START_SIMULATION);
+ else {
+ if (!SIM->test_for_text_console ())
+ return CI_ERR_NO_TEXT_CONSOLE;
+ bx_config_interface (BX_CI_START_MENU);
+ }
+ break;
+ case CI_RUNTIME_CONFIG:
+ bx_config_interface (BX_CI_RUNTIME);
+ break;
+ case CI_SHUTDOWN:
+ //fprintf (stderr, "textconfig.cc: shutdown\n");
+ break;
+ }
+ return 0;
+}
+
+// if I can make things compile without this module linked in, then
+// this file can become a plugin too.
+int init_text_config_interface ()
+{
+ //fprintf (stderr, "plugin_init for textconfig.cc\n");
+ SIM->register_configuration_interface ("textconfig", ci_callback, NULL);
+ return 0; // success
+}
diff --git a/tools/ioemu/gui/textconfig.h b/tools/ioemu/gui/textconfig.h
new file mode 100644
index 0000000000..01687ca269
--- /dev/null
+++ b/tools/ioemu/gui/textconfig.h
@@ -0,0 +1,19 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: textconfig.h,v 1.1 2002/10/29 20:16:04 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+enum {
+ BX_CI_INIT,
+ BX_CI_START_MENU,
+ BX_CI_START_OPTS,
+ BX_CI_START_OPTS_MEM,
+ BX_CI_START_OPTS_INTERFACE,
+ BX_CI_START_OPTS_DISK,
+ BX_CI_START_OPTS_SOUND,
+ BX_CI_START_OPTS_MISC,
+ BX_CI_START_SIMULATION,
+ BX_CI_RUNTIME,
+ BX_CI_N_MENUS
+};
+
+int init_text_config_interface ();
diff --git a/tools/ioemu/gui/x.cc b/tools/ioemu/gui/x.cc
new file mode 100644
index 0000000000..5d1cae917a
--- /dev/null
+++ b/tools/ioemu/gui/x.cc
@@ -0,0 +1,1848 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: x.cc,v 1.76 2003/08/11 19:27:57 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#define XK_PUBLISHING
+#define XK_TECHNICAL
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_WITH_X11
+
+extern "C" {
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <X11/Xatom.h>
+#include <X11/keysym.h>
+#if BX_HAVE_XPM_H
+#include <X11/xpm.h>
+#endif
+}
+
+#if BX_HAVE_XPM_H
+#include "icon_bochs.xpm"
+#else
+#include "icon_bochs.h"
+#endif
+
+#include "font/vga.bitmap.h"
+
+class bx_x_gui_c : public bx_gui_c {
+public:
+ bx_x_gui_c (void);
+ DECLARE_GUI_VIRTUAL_METHODS()
+#if BX_USE_IDLE_HACK
+ virtual void sim_is_idle(void);
+#endif
+};
+
+// declare one instance of the gui object and call macro to insert the
+// plugin code
+static bx_x_gui_c *theGui = NULL;
+IMPLEMENT_GUI_PLUGIN_CODE(x)
+
+#define LOG_THIS theGui->
+
+#define MAX_MAPPED_STRING_LENGTH 10
+
+/* These are used as arguments to nearly every Xlib routine, so it saves
+ * routine arguments to declare them global. If there were
+ * additional source files, they would be declared extern there. */
+Display *bx_x_display;
+int bx_x_screen_num;
+static Colormap default_cmap;
+static unsigned long white_pixel=0, black_pixel=0;
+
+static char *progname; /* name this program was invoked by */
+
+static unsigned int text_rows=25, text_cols=80;
+static Bit8u h_panning = 0, v_panning = 0;
+
+static Window win;
+static GC gc, gc_inv, gc_headerbar, gc_headerbar_inv;
+static unsigned font_width, font_height;
+static unsigned dimension_x=0, dimension_y=0;
+static unsigned vga_bpp=8;
+
+static XImage *ximage = NULL;
+static unsigned imDepth, imWide, imBPP;
+
+// current cursor coordinates
+static int prev_x=-1, prev_y=-1;
+static int current_x=-1, current_y=-1;
+static unsigned mouse_button_state = 0;
+
+static unsigned prev_cursor_x=0;
+static unsigned prev_cursor_y=0;
+
+static int warp_home_x = 200;
+static int warp_home_y = 200;
+static int mouse_enable_x = 0;
+static int mouse_enable_y = 0;
+static int warp_dx = 0;
+static int warp_dy = 0;
+
+static void warp_cursor(int dx, int dy);
+static void disable_cursor();
+static void enable_cursor();
+
+static Bit32u convertStringToXKeysym (const char *string);
+
+static bx_bool x_init_done = false;
+
+static Pixmap vgafont[256];
+
+struct {
+ Pixmap bmap;
+ unsigned xdim;
+ unsigned ydim;
+ } bx_bitmaps[BX_MAX_PIXMAPS];
+unsigned bx_bitmap_entries = 0;
+
+static struct {
+ Pixmap bitmap;
+ unsigned xdim;
+ unsigned ydim;
+ unsigned xorigin;
+ unsigned yorigin;
+ unsigned alignment;
+ void (*f)(void);
+ } bx_headerbar_entry[BX_MAX_HEADERBAR_ENTRIES];
+static unsigned bx_headerbar_y = 0;
+static unsigned bx_headerbar_entries = 0;
+static unsigned bx_bitmap_left_xorigin = 0; // pixels from left
+static unsigned bx_bitmap_right_xorigin = 0; // pixels from right
+
+static void headerbar_click(int x, int y);
+static void send_keyboard_mouse_status(void);
+
+
+
+
+Bit32u ascii_to_key_event[0x5f] = {
+ // !"#$%&'
+ BX_KEY_SPACE,
+ BX_KEY_1,
+ BX_KEY_SINGLE_QUOTE,
+ BX_KEY_3,
+ BX_KEY_4,
+ BX_KEY_5,
+ BX_KEY_7,
+ BX_KEY_SINGLE_QUOTE,
+
+ // ()*+,-./
+ BX_KEY_9,
+ BX_KEY_0,
+ BX_KEY_8,
+ BX_KEY_EQUALS,
+ BX_KEY_COMMA,
+ BX_KEY_MINUS,
+ BX_KEY_PERIOD,
+ BX_KEY_SLASH,
+
+ // 01234567
+ BX_KEY_0,
+ BX_KEY_1,
+ BX_KEY_2,
+ BX_KEY_3,
+ BX_KEY_4,
+ BX_KEY_5,
+ BX_KEY_6,
+ BX_KEY_7,
+
+ // 89:;<=>?
+ BX_KEY_8,
+ BX_KEY_9,
+ BX_KEY_SEMICOLON,
+ BX_KEY_SEMICOLON,
+ BX_KEY_COMMA,
+ BX_KEY_EQUALS,
+ BX_KEY_PERIOD,
+ BX_KEY_SLASH,
+
+ // @ABCDEFG
+ BX_KEY_2,
+ BX_KEY_A,
+ BX_KEY_B,
+ BX_KEY_C,
+ BX_KEY_D,
+ BX_KEY_E,
+ BX_KEY_F,
+ BX_KEY_G,
+
+
+ // HIJKLMNO
+ BX_KEY_H,
+ BX_KEY_I,
+ BX_KEY_J,
+ BX_KEY_K,
+ BX_KEY_L,
+ BX_KEY_M,
+ BX_KEY_N,
+ BX_KEY_O,
+
+
+ // PQRSTUVW
+ BX_KEY_P,
+ BX_KEY_Q,
+ BX_KEY_R,
+ BX_KEY_S,
+ BX_KEY_T,
+ BX_KEY_U,
+ BX_KEY_V,
+ BX_KEY_W,
+
+ // XYZ[\]^_
+ BX_KEY_X,
+ BX_KEY_Y,
+ BX_KEY_Z,
+ BX_KEY_LEFT_BRACKET,
+ BX_KEY_BACKSLASH,
+ BX_KEY_RIGHT_BRACKET,
+ BX_KEY_6,
+ BX_KEY_MINUS,
+
+ // `abcdefg
+ BX_KEY_GRAVE,
+ BX_KEY_A,
+ BX_KEY_B,
+ BX_KEY_C,
+ BX_KEY_D,
+ BX_KEY_E,
+ BX_KEY_F,
+ BX_KEY_G,
+
+ // hijklmno
+ BX_KEY_H,
+ BX_KEY_I,
+ BX_KEY_J,
+ BX_KEY_K,
+ BX_KEY_L,
+ BX_KEY_M,
+ BX_KEY_N,
+ BX_KEY_O,
+
+ // pqrstuvw
+ BX_KEY_P,
+ BX_KEY_Q,
+ BX_KEY_R,
+ BX_KEY_S,
+ BX_KEY_T,
+ BX_KEY_U,
+ BX_KEY_V,
+ BX_KEY_W,
+
+ // xyz{|}~
+ BX_KEY_X,
+ BX_KEY_Y,
+ BX_KEY_Z,
+ BX_KEY_LEFT_BRACKET,
+ BX_KEY_BACKSLASH,
+ BX_KEY_RIGHT_BRACKET,
+ BX_KEY_GRAVE
+ };
+
+extern Bit8u graphics_snapshot[32 * 1024];
+
+
+static void create_internal_vga_font(void);
+static void xkeypress(KeySym keysym, int press_release);
+// extern "C" void select_visual(void);
+
+#define ROUNDUP(nbytes, pad) ((((nbytes) + ((pad)-1)) / (pad)) * ((pad)>>3))
+
+
+#define MAX_VGA_COLORS 256
+
+unsigned long col_vals[MAX_VGA_COLORS]; // 256 VGA colors
+unsigned curr_foreground, curr_background;
+
+static unsigned x_tilesize, y_tilesize;
+
+
+// Try to allocate NCOLORS at once in the colormap provided. If it can
+// be done, return true. If not, return false. (In either case, free
+// up the color cells so that we don't add to the problem!) This is used
+// to determine whether Bochs should use a private colormap even when the
+// user did not specify it.
+static bx_bool
+test_alloc_colors (Colormap cmap, Bit32u n_tries) {
+ XColor color;
+ unsigned long pixel[MAX_VGA_COLORS];
+ bx_bool pixel_valid[MAX_VGA_COLORS];
+ Bit32u n_allocated = 0;
+ Bit32u i;
+ color.flags = DoRed | DoGreen | DoBlue;
+ for (i=0; i<n_tries; i++) {
+ // choose wierd color values that are unlikely to already be in the
+ // colormap.
+ color.red = ((i+41)%MAX_VGA_COLORS) << 8;
+ color.green = ((i+42)%MAX_VGA_COLORS) << 8;
+ color.blue = ((i+43)%MAX_VGA_COLORS) << 8;
+ pixel_valid[i] = false;
+ if (XAllocColor (bx_x_display, cmap, &color)) {
+ pixel[i] = color.pixel;
+ pixel_valid[i] = true;
+ n_allocated++;
+ }
+ }
+ BX_INFO (("test_alloc_colors: %d colors available out of %d colors tried", n_allocated, n_tries));
+ // now free them all
+ for (i=0; i<n_tries; i++) {
+ if (pixel_valid[i]) XFreeColors (bx_x_display, cmap, &pixel[i], 1, 0);
+ }
+ return (n_allocated == n_tries);
+}
+
+bx_x_gui_c::bx_x_gui_c () {
+}
+
+ void
+bx_x_gui_c::specific_init(int argc, char **argv, unsigned tilewidth, unsigned tileheight,
+ unsigned headerbar_y)
+{
+ unsigned i;
+ int x, y; /* window position */
+ unsigned int border_width = 4; /* four pixels */
+#if BX_CPU_LEVEL < 2
+ char *window_name = "Bochs 8086 emulator, http://bochs.sourceforge.net/";
+#elif BX_CPU_LEVEL == 2
+ char *window_name = "Bochs 80286 emulator, http://bochs.sourceforge.net/";
+#elif BX_CPU_LEVEL == 3
+ char *window_name = "Bochs 80386 emulator, http://bochs.sourceforge.net/";
+#elif BX_CPU_LEVEL == 4
+ char *window_name = "Bochs 80486 emulator, http://bochs.sourceforge.net/";
+#else
+ char *window_name = "VTXen";
+#endif
+ char *icon_name = "Bochs";
+ Pixmap icon_pixmap;
+#if BX_HAVE_XPM_H
+ Pixmap icon_mask;
+#endif
+ XSizeHints size_hints;
+ char *display_name = NULL;
+ /* create GC for text and drawing */
+ unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
+ XGCValues values;
+ Visual *default_visual;
+ int default_depth;
+ XEvent report;
+ XSetWindowAttributes win_attr;
+ unsigned long plane_masks_return[1];
+ XColor color;
+
+ put("XGUI");
+
+ x_tilesize = tilewidth;
+ y_tilesize = tileheight;
+ bx_headerbar_y = headerbar_y;
+
+ progname = argv[0];
+
+ /* connect to X server */
+ if ( (bx_x_display=XOpenDisplay(display_name)) == NULL )
+ {
+ BX_PANIC(("%s: cannot connect to X server %s",
+ progname, XDisplayName(display_name)));
+ }
+
+ /* get screen size from display structure macro */
+ bx_x_screen_num = DefaultScreen(bx_x_display);
+
+ /* Note that in a real application, x and y would default to 0
+ * but would be settable from the command line or resource database.
+ */
+ x = y = 0;
+
+
+ // Temporary values so we can create the window
+ font_width = 8;
+ font_height = 16;
+
+ dimension_x = text_cols * font_width;
+ dimension_y = text_rows * font_height + headerbar_y;
+
+ /* create opaque window */
+ win = XCreateSimpleWindow(bx_x_display, RootWindow(bx_x_display,bx_x_screen_num),
+ x, y,
+ dimension_x,
+ dimension_y,
+ border_width,
+ BlackPixel(bx_x_display, bx_x_screen_num),
+ BlackPixel(bx_x_display, bx_x_screen_num));
+
+ // (attempt to) enable backing store
+ win_attr.save_under=1;
+ win_attr.backing_store=Always;
+ XChangeWindowAttributes(bx_x_display,win,CWSaveUnder|CWBackingStore,&win_attr);
+
+ default_depth = DefaultDepth(bx_x_display, bx_x_screen_num);
+ default_visual = DefaultVisual(bx_x_display, bx_x_screen_num);
+
+ if (!bx_options.Oprivate_colormap->get ()) {
+ default_cmap = DefaultColormap(bx_x_display, bx_x_screen_num);
+ // try to use default colormap. If not enough colors are available,
+ // then switch to private colormap despite the user setting. There
+ // are too many cases when no colors are available and Bochs simply
+ // draws everything in black on black.
+ if (!test_alloc_colors (default_cmap, 16)) {
+ BX_ERROR (("I can't even allocate 16 colors! Switching to a private colormap"));
+ bx_options.Oprivate_colormap->set (1);
+ }
+ col_vals[0] = BlackPixel(bx_x_display, bx_x_screen_num);
+ col_vals[15] = WhitePixel(bx_x_display, bx_x_screen_num);
+ for (i = 1; i < MAX_VGA_COLORS; i++) {
+ if (i==15) continue;
+ col_vals[i] = col_vals[0];
+ }
+ }
+
+ if (bx_options.Oprivate_colormap->get ()) {
+ default_cmap = XCreateColormap(bx_x_display, DefaultRootWindow(bx_x_display),
+ default_visual, AllocNone);
+ if (XAllocColorCells(bx_x_display, default_cmap, False,
+ plane_masks_return, 0, col_vals, MAX_VGA_COLORS) == 0) {
+ BX_PANIC(("XAllocColorCells returns error. Maybe your screen does not support a private colormap?"));
+ }
+
+ win_attr.colormap = default_cmap;
+ XChangeWindowAttributes(bx_x_display, win, CWColormap, &win_attr);
+
+ color.flags = DoRed | DoGreen | DoBlue;
+
+ for (i=0; i < MAX_VGA_COLORS; i++) {
+ color.pixel = i;
+ if (i==15) {
+ color.red = 0xffff;
+ color.green = 0xffff;
+ color.blue = 0xffff;
+ }
+ else {
+ color.red = 0;
+ color.green = 0;
+ color.blue = 0;
+ }
+ XStoreColor(bx_x_display, default_cmap, &color);
+ }
+ }
+
+ // convenience variables which hold the black & white color indeces
+ black_pixel = col_vals[0];
+ white_pixel = col_vals[15];
+
+ BX_INFO(("font %u wide x %u high, display depth = %d",
+ (unsigned) font_width, (unsigned) font_height, default_depth));
+
+ //select_visual();
+
+
+ /* Get available icon sizes from Window manager */
+
+#if BX_HAVE_XPM_H
+ /* Create pixmap from XPM for icon */
+ XCreatePixmapFromData(bx_x_display, win, icon_bochs_xpm, &icon_pixmap, &icon_mask, NULL);
+#else
+ /* Create pixmap of depth 1 (bitmap) for icon */
+ icon_pixmap = XCreateBitmapFromData(bx_x_display, win,
+ (char *) bochs_icon_bits, bochs_icon_width, bochs_icon_height);
+#endif
+
+ /* Set size hints for window manager. The window manager may
+ * override these settings. Note that in a real
+ * application if size or position were set by the user
+ * the flags would be UPosition and USize, and these would
+ * override the window manager's preferences for this window. */
+ /* x, y, width, and height hints are now taken from
+ * the actual settings of the window when mapped. Note
+ * that PPosition and PSize must be specified anyway. */
+
+ size_hints.flags = PPosition | PSize | PMinSize | PMaxSize;
+ size_hints.max_width = size_hints.min_width = dimension_x;
+ size_hints.max_height = size_hints.min_height = dimension_y;
+
+ {
+ XWMHints wm_hints;
+ XClassHint class_hints;
+
+ /* format of the window name and icon name
+ * arguments has changed in R4 */
+ XTextProperty windowName, iconName;
+
+ /* These calls store window_name and icon_name into
+ * XTextProperty structures and set their other
+ * fields properly. */
+ if (XStringListToTextProperty(&window_name, 1, &windowName) == 0) {
+ BX_PANIC(("%s: structure allocation for windowName failed.",
+ progname));
+ }
+
+ if (XStringListToTextProperty(&icon_name, 1, &iconName) == 0) {
+ BX_PANIC(("%s: structure allocation for iconName failed.",
+ progname));
+ }
+
+ wm_hints.initial_state = NormalState;
+ wm_hints.input = True;
+ wm_hints.icon_pixmap = icon_pixmap;
+#if BX_HAVE_XPM_H
+ wm_hints.icon_mask = icon_mask;
+ wm_hints.flags = StateHint | IconPixmapHint | IconMaskHint | InputHint;
+#else
+ wm_hints.flags = StateHint | IconPixmapHint | InputHint;
+#endif
+ class_hints.res_name = progname;
+ class_hints.res_class = "Bochs";
+
+ XSetWMProperties(bx_x_display, win, &windowName, &iconName,
+ argv, argc, &size_hints, &wm_hints,
+ &class_hints);
+ }
+
+ /* Select event types wanted */
+ XSelectInput(bx_x_display, win, ExposureMask | KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask | StructureNotifyMask | PointerMotionMask |
+ EnterWindowMask | LeaveWindowMask );
+
+
+ /* Create default Graphics Context */
+ gc = XCreateGC(bx_x_display, win, valuemask, &values);
+ gc_inv = XCreateGC(bx_x_display, win, valuemask, &values);
+ gc_headerbar = XCreateGC(bx_x_display, win, valuemask, &values);
+ gc_headerbar_inv = XCreateGC(bx_x_display, win, valuemask, &values);
+
+ XSetState(bx_x_display, gc, white_pixel, black_pixel, GXcopy,AllPlanes);
+
+ XSetState(bx_x_display, gc_inv, black_pixel, white_pixel, GXinvert,AllPlanes);
+
+ XSetState(bx_x_display, gc_headerbar, black_pixel, white_pixel, GXcopy,AllPlanes);
+
+ XSetState(bx_x_display, gc_headerbar_inv, white_pixel, black_pixel, GXcopy,AllPlanes);
+
+
+ /* Display window */
+ XMapWindow(bx_x_display, win);
+ XSync(bx_x_display, /* no discard */ 0);
+
+ BX_DEBUG(("waiting for MapNotify"));
+ while (1) {
+ XNextEvent(bx_x_display, &report);
+ if (report.type == MapNotify) break;
+ }
+ BX_DEBUG(("MapNotify found."));
+
+ // Create the VGA font
+ create_internal_vga_font();
+
+
+{
+ char *imagedata;
+
+ ximage = XCreateImage(bx_x_display, default_visual,
+ default_depth, // depth of image (bitplanes)
+ ZPixmap,
+ 0, // offset
+ NULL, // malloc() space after
+ x_tilesize, y_tilesize, // x & y size of image
+ 32, // # bits of padding
+ 0 ); // bytes_per_line, let X11 calculate
+ if (!ximage)
+ BX_PANIC(("vga: couldn't XCreateImage()"));
+
+ imDepth = default_depth;
+ imWide = ximage->bytes_per_line;
+ imBPP = ximage->bits_per_pixel;
+
+ imagedata = (char *) malloc( (size_t) (ximage->bytes_per_line * y_tilesize) );
+ if (!imagedata) BX_PANIC(("imagedata: malloc returned error"));
+
+ ximage->data = imagedata;
+
+ if (imBPP < imDepth) {
+ BX_PANIC(("vga_x: bits_per_pixel < depth ?"));
+ }
+
+ x_init_done = true;
+
+}
+
+ curr_background = 0;
+ XSetBackground(bx_x_display, gc, col_vals[curr_background]);
+ curr_foreground = 1;
+ XSetForeground(bx_x_display, gc, col_vals[curr_foreground]);
+ //XGrabPointer( bx_x_display, win, True, 0, GrabModeAsync, GrabModeAsync,
+ // win, None, CurrentTime );
+
+
+ XFlush(bx_x_display);
+
+ // loads keymap for x11
+ if(bx_options.keyboard.OuseMapping->get()) {
+ bx_keymap.loadKeymap(convertStringToXKeysym);
+ }
+}
+
+
+// This is called whenever the mouse_enabled parameter changes. It
+// can change because of a gui event such as clicking on the mouse-enable
+// bitmap or pressing the middle button, or from the configuration interface.
+// In all those cases, setting the parameter value will get you here.
+ void
+bx_x_gui_c::mouse_enabled_changed_specific (bx_bool val)
+{
+ BX_DEBUG (("mouse_enabled=%d, x11 specific code", val?1:0));
+ if (val) {
+ BX_INFO(("[x] Mouse on"));
+ mouse_enable_x = current_x;
+ mouse_enable_y = current_y;
+ disable_cursor();
+ // Move the cursor to a 'safe' place
+ warp_cursor(warp_home_x-current_x, warp_home_y-current_y);
+ } else {
+ BX_INFO(("[x] Mouse off"));
+ enable_cursor();
+ warp_cursor(mouse_enable_x-current_x, mouse_enable_y-current_y);
+ }
+}
+
+ void
+create_internal_vga_font(void)
+{
+ // Default values
+ font_width=8;
+ font_height=16;
+
+ for(int i=0; i<256; i++) {
+ vgafont[i]=XCreateBitmapFromData(bx_x_display, win, (const char*)bx_vgafont[i].data,
+ font_width, font_height);
+ if(vgafont[i] == None)
+ BX_PANIC(("Can't create vga font [%d]", i));
+ }
+}
+
+ void
+bx_x_gui_c::handle_events(void)
+{
+ XEvent report;
+ XKeyEvent *key_event;
+ KeySym keysym;
+ XComposeStatus compose;
+ char buffer[MAX_MAPPED_STRING_LENGTH];
+ int bufsize = MAX_MAPPED_STRING_LENGTH;
+ int charcount;
+ bx_bool mouse_update;
+ int y, height;
+
+
+ XPointerMovedEvent *pointer_event;
+ XEnterWindowEvent *enter_event;
+ XLeaveWindowEvent *leave_event;
+ XButtonEvent *button_event;
+ XExposeEvent *expose_event;
+
+
+ //current_x = -1;
+ //current_y = -1;
+ mouse_update = 0;
+
+ while (XPending(bx_x_display) > 0) {
+ XNextEvent(bx_x_display, &report);
+ switch (report.type) {
+
+ case Expose:
+ expose_event = &report.xexpose;
+ /* Adjust y, and reduce height if it overlaps headerbar. */
+ y = expose_event->y - BX_HEADER_BAR_Y;
+ height = expose_event->height;
+ if (y < 0) {
+ height += y;
+ y = 0;
+ }
+
+ DEV_vga_redraw_area(
+ (unsigned) expose_event->x,
+ y,
+ (unsigned) expose_event->width,
+ height);
+
+ /* Always draw headerbar, even if not touched by expose event.
+ * As a small optimization, only do it on last contigous expose.
+ */
+ if (expose_event->count == 0) {
+ show_headerbar();
+ }
+ break;
+
+ case ConfigureNotify:
+ BX_DEBUG(("ConfigureNotify Xevent"));
+ /* FIXME: It's not clear why we have to show the headerbar here.
+ * This should be forced by the following expose events.
+ */
+ show_headerbar();
+ break;
+
+ case ButtonPress:
+ button_event = (XButtonEvent *) &report;
+ BX_DEBUG(("xxx: buttonpress"));
+ if (button_event->y < BX_HEADER_BAR_Y) {
+ BX_DEBUG(("xxx: in headerbar"));
+ if (mouse_update) {
+ BX_DEBUG(("xxx: mouse_update=1"));
+ send_keyboard_mouse_status();
+ mouse_update = 0;
+ }
+ prev_x = current_x = -1;
+ prev_y = current_y = -1;
+ headerbar_click(button_event->x, button_event->y);
+ break;
+ }
+ current_x = button_event->x;
+ current_y = button_event->y;
+ mouse_update = 1;
+ BX_DEBUG(("xxx: x,y=(%d,%d)", current_x, current_y));
+ switch (button_event->button) {
+ case Button1:
+ BX_DEBUG(("xxx: button1"));
+ mouse_button_state |= 0x01;
+ send_keyboard_mouse_status();
+ mouse_update = 0;
+ break;
+ case Button2:
+ BX_DEBUG(("XXX: button2"));
+
+ // (mch) Hack for easier mouse handling (toggle mouse enable)
+ toggle_mouse_enable();
+
+ //mouse_button_state |= ;
+ //send_keyboard_mouse_status();
+ //mouse_update = 0;
+ break;
+ case Button3:
+ BX_DEBUG(("xxx: button3"));
+ mouse_button_state |= 0x02;
+ send_keyboard_mouse_status();
+ mouse_update = 0;
+ break;
+ }
+ break;
+
+ case ButtonRelease:
+ button_event = (XButtonEvent *) &report;
+//BX_INFO(("xxx: buttonrelease"));
+ if (button_event->y < BX_HEADER_BAR_Y) {
+//BX_INFO(("xxx: in headerbar"));
+ if (mouse_update) {
+//BX_INFO(("xxx: mouse_update=1"));
+ send_keyboard_mouse_status();
+ mouse_update = 0;
+ }
+ prev_x = current_x = -1;
+ prev_y = current_y = -1;
+ // ignore, in headerbar area
+ break;
+ }
+ current_x = button_event->x;
+ current_y = button_event->y;
+ mouse_update = 1;
+//BX_INFO(("xxx: x,y=(%d,%d)", current_x, current_y));
+ switch (button_event->button) {
+ case Button1:
+//BX_INFO(("xxx: button1"));
+ mouse_button_state &= ~0x01;
+ send_keyboard_mouse_status();
+ mouse_update = 0;
+ break;
+ case Button2:
+//BX_INFO(("xxx: button2"));
+ //mouse_button_state &= ~;
+ //send_keyboard_mouse_status();
+ //mouse_update = 0;
+ break;
+ case Button3:
+//BX_INFO(("xxx: button3"));
+ mouse_button_state &= ~0x02;
+ send_keyboard_mouse_status();
+ mouse_update = 0;
+ break;
+ }
+ break;
+
+ case KeyPress:
+ key_event = (XKeyEvent *) &report;
+ charcount = XLookupString(key_event, buffer, bufsize, &keysym, &compose);
+ xkeypress(keysym, 0);
+ break;
+
+ case KeyRelease:
+ key_event = (XKeyEvent *) &report;
+ charcount = XLookupString(key_event, buffer, bufsize, &keysym, &compose);
+ xkeypress(keysym, 1);
+ break;
+
+ case MotionNotify:
+ pointer_event = (XPointerMovedEvent *) &report;
+ current_x = pointer_event->x;
+ current_y = pointer_event->y;
+ mouse_update = 1;
+//BX_INFO(("xxx: motionNotify x,y=(%d,%d)", current_x, current_y));
+ break;
+
+ case EnterNotify:
+ enter_event = (XEnterWindowEvent *) &report;
+ prev_x = current_x = enter_event->x;
+ prev_y = current_y = enter_event->y;
+//BX_INFO(("xxx: enterNotify x,y=(%d,%d)", current_x, current_y));
+ break;
+
+ case LeaveNotify:
+ leave_event = (XLeaveWindowEvent *) &report;
+ prev_x = current_x = -1;
+ prev_y = current_y = -1;
+//BX_INFO(("xxx: LeaveNotify x,y set to -1"));
+ break;
+
+ case MapNotify:
+ /* screen needs redraw, since X would have tossed previous
+ * requests before window mapped
+ */
+//BX_INFO(("xxx: mapnotify: found"));
+ //retval = 1;
+ break;
+
+ default:
+ // (mch) Ignore...
+ BX_DEBUG(("XXX: default Xevent type"));
+ /* all events selected by StructureNotifyMask are thrown away here,
+ * since nothing is done with them */
+ break;
+ } /* end switch */
+ } /* end while */
+
+ if (mouse_update) {
+ BX_DEBUG(("XXX: bottom, send status"));
+ send_keyboard_mouse_status();
+ }
+}
+
+
+ void
+send_keyboard_mouse_status(void)
+{
+ BX_DEBUG(("XXX: prev=(%d,%d) curr=(%d,%d)",
+ prev_x, prev_y, current_x, current_y));
+
+ if ( (prev_x!=-1) && (current_x!=-1) && (prev_y!=-1) && (current_y!=-1)) {
+ int dx, dy;
+
+ // (mch) consider warping here
+ dx = current_x - prev_x - warp_dx;
+ dy = -(current_y - prev_y - warp_dy);
+ warp_cursor(warp_home_x-current_x, warp_home_y-current_y);
+
+//BX_INFO(("xxx: MOUSE_MOTION: dx=%d, dy=%d", (int) dx, (int) dy));
+ DEV_mouse_motion (dx, dy, mouse_button_state);
+ //if (warped) {
+ // prev_x = current_x = -1;
+ // prev_y = current_y = -1;
+ // }
+ //else {
+ prev_x = current_x;
+ prev_y = current_y;
+ // }
+ }
+ else {
+ if ( (current_x!=-1) && (current_y!=-1)) {
+ prev_x = current_x;
+ prev_y = current_y;
+ }
+ else {
+ prev_x = current_x = -1;
+ prev_y = current_y = -1;
+ }
+ }
+}
+
+ void
+bx_x_gui_c::flush(void)
+{
+ if (bx_x_display)
+ XFlush(bx_x_display);
+}
+
+
+ void
+xkeypress(KeySym keysym, int press_release)
+{
+ Bit32u key_event;
+
+ /* Old (no mapping) behavior */
+ if(!bx_options.keyboard.OuseMapping->get()){
+
+ // this depends on the fact that the X11 keysyms which
+ // correspond to the ascii characters space .. tilde
+ // are in consequtive order.
+ if ((keysym >= XK_space) && (keysym <= XK_asciitilde)) {
+ key_event = ascii_to_key_event[keysym - XK_space];
+ }
+ else switch (keysym) {
+ case XK_KP_1:
+#ifdef XK_KP_End
+ case XK_KP_End:
+#endif
+ key_event = BX_KEY_KP_END; break;
+
+ case XK_KP_2:
+#ifdef XK_KP_Down
+ case XK_KP_Down:
+#endif
+ key_event = BX_KEY_KP_DOWN; break;
+
+ case XK_KP_3:
+#ifdef XK_KP_Page_Down
+ case XK_KP_Page_Down:
+#endif
+ key_event = BX_KEY_KP_PAGE_DOWN; break;
+
+ case XK_KP_4:
+#ifdef XK_KP_Left
+ case XK_KP_Left:
+#endif
+ key_event = BX_KEY_KP_LEFT; break;
+
+ case XK_KP_5:
+#ifdef XK_KP_Begin
+ case XK_KP_Begin:
+#endif
+ key_event = BX_KEY_KP_5; break;
+
+ case XK_KP_6:
+#ifdef XK_KP_Right
+ case XK_KP_Right:
+#endif
+ key_event = BX_KEY_KP_RIGHT; break;
+
+ case XK_KP_7:
+#ifdef XK_KP_Home
+ case XK_KP_Home:
+#endif
+ key_event = BX_KEY_KP_HOME; break;
+
+ case XK_KP_8:
+#ifdef XK_KP_Up
+ case XK_KP_Up:
+#endif
+ key_event = BX_KEY_KP_UP; break;
+
+ case XK_KP_9:
+#ifdef XK_KP_Page_Up
+ case XK_KP_Page_Up:
+#endif
+ key_event = BX_KEY_KP_PAGE_UP; break;
+
+ case XK_KP_0:
+#ifdef XK_KP_Insert
+ case XK_KP_Insert:
+#endif
+ key_event = BX_KEY_KP_INSERT; break;
+
+ case XK_KP_Decimal:
+#ifdef XK_KP_Delete
+ case XK_KP_Delete:
+#endif
+ key_event = BX_KEY_KP_DELETE; break;
+
+#ifdef XK_KP_Enter
+ case XK_KP_Enter: key_event = BX_KEY_KP_ENTER; break;
+#endif
+
+ case XK_KP_Subtract: key_event = BX_KEY_KP_SUBTRACT; break;
+ case XK_KP_Add: key_event = BX_KEY_KP_ADD; break;
+
+ case XK_KP_Multiply: key_event = BX_KEY_KP_MULTIPLY; break;
+ case XK_KP_Divide: key_event = BX_KEY_KP_DIVIDE; break;
+
+
+ case XK_Up: key_event = BX_KEY_UP; break;
+ case XK_Down: key_event = BX_KEY_DOWN; break;
+ case XK_Left: key_event = BX_KEY_LEFT; break;
+ case XK_Right: key_event = BX_KEY_RIGHT; break;
+
+
+ case XK_Delete: key_event = BX_KEY_DELETE; break;
+ case XK_BackSpace: key_event = BX_KEY_BACKSPACE; break;
+ case XK_Tab: key_event = BX_KEY_TAB; break;
+#ifdef XK_ISO_Left_Tab
+ case XK_ISO_Left_Tab: key_event = BX_KEY_TAB; break;
+#endif
+ case XK_Return: key_event = BX_KEY_ENTER; break;
+ case XK_Escape: key_event = BX_KEY_ESC; break;
+ case XK_F1: key_event = BX_KEY_F1; break;
+ case XK_F2: key_event = BX_KEY_F2; break;
+ case XK_F3: key_event = BX_KEY_F3; break;
+ case XK_F4: key_event = BX_KEY_F4; break;
+ case XK_F5: key_event = BX_KEY_F5; break;
+ case XK_F6: key_event = BX_KEY_F6; break;
+ case XK_F7: key_event = BX_KEY_F7; break;
+ case XK_F8: key_event = BX_KEY_F8; break;
+ case XK_F9: key_event = BX_KEY_F9; break;
+ case XK_F10: key_event = BX_KEY_F10; break;
+ case XK_F11: key_event = BX_KEY_F11; break;
+ case XK_F12: key_event = BX_KEY_F12; break;
+ case XK_Control_L: key_event = BX_KEY_CTRL_L; break;
+#ifdef XK_Control_R
+ case XK_Control_R: key_event = BX_KEY_CTRL_R; break;
+#endif
+ case XK_Shift_L: key_event = BX_KEY_SHIFT_L; break;
+ case XK_Shift_R: key_event = BX_KEY_SHIFT_R; break;
+ case XK_Alt_L: key_event = BX_KEY_ALT_L; break;
+#ifdef XK_Alt_R
+ case XK_Alt_R: key_event = BX_KEY_ALT_R; break;
+#endif
+ case XK_Caps_Lock: key_event = BX_KEY_CAPS_LOCK; break;
+ case XK_Num_Lock: key_event = BX_KEY_NUM_LOCK; break;
+#ifdef XK_Scroll_Lock
+ case XK_Scroll_Lock: key_event = BX_KEY_SCRL_LOCK; break;
+#endif
+#ifdef XK_Print
+ case XK_Print: key_event = BX_KEY_PRINT; break;
+#endif
+#ifdef XK_Pause
+ case XK_Pause: key_event = BX_KEY_PAUSE; break;
+#endif
+
+ case XK_Insert: key_event = BX_KEY_INSERT; break;
+ case XK_Home: key_event = BX_KEY_HOME; break;
+ case XK_End: key_event = BX_KEY_END; break;
+ case XK_Page_Up: key_event = BX_KEY_PAGE_UP; break;
+ case XK_Page_Down: key_event = BX_KEY_PAGE_DOWN; break;
+
+ default:
+ BX_ERROR(( "xkeypress(): keysym %x unhandled!", (unsigned) keysym ));
+ return;
+ break;
+ }
+ }
+ else {
+ /* use mapping */
+ BXKeyEntry *entry = bx_keymap.findHostKey (keysym);
+ if (!entry) {
+ BX_ERROR(( "xkeypress(): keysym %x unhandled!", (unsigned) keysym ));
+ return;
+ }
+ key_event = entry->baseKey;
+ }
+
+ if (press_release)
+ key_event |= BX_KEY_RELEASED;
+
+ DEV_kbd_gen_scancode(key_event);
+}
+
+
+ void
+bx_x_gui_c::clear_screen(void)
+{
+ XClearArea(bx_x_display, win, 0, bx_headerbar_y, dimension_x, dimension_y-bx_headerbar_y, 0);
+}
+
+
+
+
+ void
+bx_x_gui_c::text_update(Bit8u *old_text, Bit8u *new_text,
+ unsigned long cursor_x, unsigned long cursor_y,
+ bx_vga_tminfo_t tm_info, unsigned nrows)
+{
+ unsigned char *old_line, *new_line;
+ unsigned char cChar;
+ unsigned int curs, hchars, i, j, offset, rows, x, y, xc, yc, yc2;
+ unsigned new_foreground, new_background;
+ Bit8u cfwidth, cfheight, cfheight2, font_col, font_row, font_row2;
+ bx_bool force_update=0;
+ unsigned char cell[64];
+
+ UNUSED(nrows);
+ if (charmap_updated) {
+ BX_INFO(("charmap update. Font Height is %d",font_height));
+ for (unsigned c = 0; c<256; c++) {
+ if (char_changed[c]) {
+ XFreePixmap(bx_x_display, vgafont[c]);
+ bx_bool gfxchar = tm_info.line_graphics && ((c & 0xE0) == 0xC0);
+ j = 0;
+ memset(cell, 0, sizeof(cell));
+ for(i=0; i<font_height*2; i+=2) {
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x01)<<7);
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x02)<<5);
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x04)<<3);
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x08)<<1);
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x10)>>1);
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x20)>>3);
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x40)>>5);
+ cell[i] |= ((vga_charmap[(c<<5)+j] & 0x80)>>7);
+ if (gfxchar) {
+ cell[i+1] = (vga_charmap[(c<<5)+j] & 0x01);
+ }
+ j++;
+ }
+
+ vgafont[c]=XCreateBitmapFromData(bx_x_display, win,
+ (const char*)cell,
+ font_width, font_height);
+ if(vgafont[c] == None)
+ BX_PANIC(("Can't create vga font [%d]", c));
+ char_changed[c] = 0;
+ }
+ }
+ force_update = 1;
+ charmap_updated = 0;
+ }
+
+ if((tm_info.h_panning != h_panning) || (tm_info.v_panning != v_panning)) {
+ force_update = 1;
+ h_panning = tm_info.h_panning;
+ v_panning = tm_info.v_panning;
+ }
+
+ // first invalidate character at previous and new cursor location
+ if ( (prev_cursor_y < text_rows) && (prev_cursor_x < text_cols) ) {
+ curs = prev_cursor_y * tm_info.line_offset + prev_cursor_x * 2;
+ old_text[curs] = ~new_text[curs];
+ }
+ if((tm_info.cs_start <= tm_info.cs_end) && (tm_info.cs_start < font_height) &&
+ (cursor_y < text_rows) && (cursor_x < text_cols)) {
+ curs = cursor_y * tm_info.line_offset + cursor_x * 2;
+ old_text[curs] = ~new_text[curs];
+ } else {
+ curs = 0xffff;
+ }
+
+ rows = text_rows;
+ if (v_panning) rows++;
+ y = 0;
+ do {
+ hchars = text_cols;
+ if (h_panning) hchars++;
+ if (v_panning) {
+ if (y == 0) {
+ yc = bx_headerbar_y;
+ font_row = v_panning;
+ cfheight = font_height - v_panning;
+ } else {
+ yc = y * font_height + bx_headerbar_y - v_panning;
+ font_row = 0;
+ if (rows == 1) {
+ cfheight = v_panning;
+ } else {
+ cfheight = font_height;
+ }
+ }
+ } else {
+ yc = y * font_height + bx_headerbar_y;
+ font_row = 0;
+ cfheight = font_height;
+ }
+ new_line = new_text;
+ old_line = old_text;
+ x = 0;
+ offset = y * tm_info.line_offset;
+ do {
+ if (h_panning) {
+ if (hchars > text_cols) {
+ xc = 0;
+ font_col = h_panning;
+ cfwidth = font_width - h_panning;
+ } else {
+ xc = x * font_width - h_panning;
+ font_col = 0;
+ if (hchars == 1) {
+ cfwidth = h_panning;
+ } else {
+ cfwidth = font_width;
+ }
+ }
+ } else {
+ xc = x * font_width;
+ font_col = 0;
+ cfwidth = font_width;
+ }
+ if ( force_update || (old_text[0] != new_text[0])
+ || (old_text[1] != new_text[1]) ) {
+
+ cChar = new_text[0];
+ new_foreground = new_text[1] & 0x0f;
+ new_background = (new_text[1] & 0xf0) >> 4;
+
+ XSetForeground(bx_x_display, gc, col_vals[DEV_vga_get_actl_pal_idx(new_foreground)]);
+ XSetBackground(bx_x_display, gc, col_vals[DEV_vga_get_actl_pal_idx(new_background)]);
+
+ XCopyPlane(bx_x_display, vgafont[cChar], win, gc, font_col, font_row, cfwidth, cfheight,
+ xc, yc, 1);
+ if (offset == curs) {
+ XSetForeground(bx_x_display, gc, col_vals[DEV_vga_get_actl_pal_idx(new_background)]);
+ XSetBackground(bx_x_display, gc, col_vals[DEV_vga_get_actl_pal_idx(new_foreground)]);
+ if (font_row == 0) {
+ yc2 = yc + tm_info.cs_start;
+ font_row2 = tm_info.cs_start;
+ cfheight2 = tm_info.cs_end - tm_info.cs_start + 1;
+ } else {
+ if (v_panning > tm_info.cs_start) {
+ yc2 = yc;
+ font_row2 = font_row;
+ cfheight2 = tm_info.cs_end - v_panning + 1;
+ } else {
+ yc2 = yc + tm_info.cs_start - v_panning;
+ font_row2 = tm_info.cs_start;
+ cfheight2 = tm_info.cs_end - tm_info.cs_start + 1;
+ }
+ }
+ XCopyPlane(bx_x_display, vgafont[cChar], win, gc, font_col, font_row2, cfwidth,
+ cfheight2, xc, yc2, 1);
+ }
+ }
+ x++;
+ new_text+=2;
+ old_text+=2;
+ offset+=2;
+ } while (--hchars);
+ y++;
+ new_text = new_line + tm_info.line_offset;
+ old_text = old_line + tm_info.line_offset;
+ } while (--rows);
+
+ prev_cursor_x = cursor_x;
+ prev_cursor_y = cursor_y;
+
+ XFlush(bx_x_display);
+}
+
+ int
+bx_x_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes)
+{
+ int len;
+ Bit8u *tmp = (Bit8u *)XFetchBytes (bx_x_display, &len);
+ // according to man XFetchBytes, tmp must be freed by XFree(). So allocate
+ // a new buffer with "new". The keyboard code will free it with delete []
+ // when the paste is done.
+ Bit8u *buf = new Bit8u[len];
+ memcpy (buf, tmp, len);
+ *bytes = buf;
+ *nbytes = len;
+ XFree (tmp);
+ return 1;
+}
+
+ int
+bx_x_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len)
+{
+ // this writes data to the clipboard.
+ BX_INFO (("storing %d bytes to X windows clipboard", len));
+ XSetSelectionOwner(bx_x_display, XA_PRIMARY, None, CurrentTime);
+ XStoreBytes (bx_x_display, (char *)text_snapshot, len);
+ return 1;
+}
+
+
+ void
+bx_x_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
+{
+ unsigned x, y;
+ unsigned color, offset;
+ Bit8u b0, b1, b2, b3;
+
+ Bit16u *tile16 = (Bit16u *)tile;
+ switch (vga_bpp) {
+ case 32: // 32 bits per pixel
+ if (ximage->byte_order == LSBFirst) {
+ memcpy(&ximage->data[0], tile, x_tilesize*y_tilesize*4);
+ }
+ else { // MSBFirst
+ for (y=0; y<y_tilesize; y++) {
+ for (x=0; x<x_tilesize; x++) {
+ offset = imWide*y + 4*x;
+ ximage->data[offset + 0] = tile[(y*x_tilesize + x)*4 + 3];
+ ximage->data[offset + 1] = tile[(y*x_tilesize + x)*4 + 2];
+ ximage->data[offset + 2] = tile[(y*x_tilesize + x)*4 + 1];
+ ximage->data[offset + 3] = tile[(y*x_tilesize + x)*4];
+ }
+ }
+ }
+ break;
+ case 24: // 24 bits per pixel
+ for (y=0; y<y_tilesize; y++) {
+ for (x=0; x<x_tilesize; x++) {
+ switch (imBPP) {
+ case 24: // 24 bits per pixel
+ offset = imWide*y + 3*x;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = tile[(y*x_tilesize + x)*3];
+ ximage->data[offset + 1] = tile[(y*x_tilesize + x)*3 + 1];
+ ximage->data[offset + 2] = tile[(y*x_tilesize + x)*3 + 2];
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = tile[(y*x_tilesize + x)*3 + 2];
+ ximage->data[offset + 1] = tile[(y*x_tilesize + x)*3 + 1];
+ ximage->data[offset + 2] = tile[(y*x_tilesize + x)*3];
+ }
+ break;
+ case 32: // 32 bits per pixel
+ offset = imWide*y + 4*x;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = tile[(y*x_tilesize + x)*3];
+ ximage->data[offset + 1] = tile[(y*x_tilesize + x)*3 + 1];
+ ximage->data[offset + 2] = tile[(y*x_tilesize + x)*3 + 2];
+ ximage->data[offset + 3] = 0;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = 0;
+ ximage->data[offset + 1] = tile[(y*x_tilesize + x)*3 + 2];
+ ximage->data[offset + 2] = tile[(y*x_tilesize + x)*3 + 1];
+ ximage->data[offset + 3] = tile[(y*x_tilesize + x)*3];
+ }
+ break;
+ }
+ }
+ }
+ break;
+ case 16: // 16 bits per pixel
+ for (y=0; y<y_tilesize; y++) {
+ for (x=0; x<x_tilesize; x++) {
+ switch (imBPP) {
+ case 16: // 16 bits per pixel
+ offset = imWide*y + 2*x;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = tile[(y*x_tilesize + x)*2];
+ ximage->data[offset + 1] = tile[(y*x_tilesize + x)*2 + 1];
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = tile[(y*x_tilesize + x)*2 + 1];
+ ximage->data[offset + 1] = tile[(y*x_tilesize + x)*2];
+ }
+ break;
+ case 24: // 24 bits per pixel
+ offset = imWide*y + 3*x;
+ b0 = (tile16[y*x_tilesize + x] & 0x001f) << 3;
+ b1 = (tile16[y*x_tilesize + x] & 0x07e0) >> 3;
+ b2 = (tile16[y*x_tilesize + x] & 0xF800) >> 8;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b2;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = b2;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b0;
+ }
+ break;
+ case 32: // 32 bits per pixel
+ offset = imWide*y + 4*x;
+ b0 = (tile16[y*x_tilesize + x] & 0x001f) << 3;
+ b1 = (tile16[y*x_tilesize + x] & 0x07e0) >> 3;
+ b2 = (tile16[y*x_tilesize + x] & 0xF800) >> 8;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b2;
+ ximage->data[offset + 3] = 0;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = 0;
+ ximage->data[offset + 1] = b2;
+ ximage->data[offset + 2] = b1;
+ ximage->data[offset + 3] = b0;
+ }
+ break;
+ }
+ }
+ }
+ break;
+ case 15: // 15 bits per pixel
+ for (y=0; y<y_tilesize; y++) {
+ for (x=0; x<x_tilesize; x++) {
+ switch (imBPP) {
+ case 16: // 16 bits per pixel
+ offset = imWide*y + 2*x;
+ b0 = (tile16[y*x_tilesize + x] & 0x001f);
+ b0 |= (tile16[y*x_tilesize + x] & 0x0060) << 1;
+ b1 = (tile16[y*x_tilesize + x] & 0x7f80) >> 7;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = b1;
+ ximage->data[offset + 1] = b0;
+ }
+ break;
+ case 24: // 24 bits per pixel
+ offset = imWide*y + 3*x;
+ b0 = (tile16[y*x_tilesize + x] & 0x001f) << 3;
+ b1 = (tile16[y*x_tilesize + x] & 0x03e0) >> 2;
+ b2 = (tile16[y*x_tilesize + x] & 0x7c00) >> 7;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b2;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = b2;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b0;
+ }
+ break;
+ case 32: // 32 bits per pixel
+ offset = imWide*y + 4*x;
+ b0 = (tile16[y*x_tilesize + x] & 0x001f) << 3;
+ b1 = (tile16[y*x_tilesize + x] & 0x03e0) >> 2;
+ b2 = (tile16[y*x_tilesize + x] & 0x7c00) >> 7;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b2;
+ ximage->data[offset + 3] = 0;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = 0;
+ ximage->data[offset + 1] = b2;
+ ximage->data[offset + 2] = b1;
+ ximage->data[offset + 3] = b0;
+ }
+ break;
+ }
+ }
+ }
+ break;
+ default: // 8 bits per pixel
+ for (y=0; y<y_tilesize; y++) {
+ for (x=0; x<x_tilesize; x++) {
+ color = col_vals[tile[y*x_tilesize + x]];
+ switch (imBPP) {
+ case 8: // 8 bits per pixel
+ ximage->data[imWide*y + x] = color;
+ break;
+ case 16: // 16 bits per pixel
+ offset = imWide*y + 2*x;
+ b0 = color >> 0;
+ b1 = color >> 8;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = b1;
+ ximage->data[offset + 1] = b0;
+ }
+ break;
+ case 24: // 24 bits per pixel
+ offset = imWide*y + 3*x;
+ b0 = color >> 0;
+ b1 = color >> 8;
+ b2 = color >> 16;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b2;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = b2;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b0;
+ }
+ break;
+ case 32: // 32 bits per pixel
+ offset = imWide*y + 4*x;
+ b0 = color >> 0;
+ b1 = color >> 8;
+ b2 = color >> 16;
+ b3 = color >> 24;
+ if (ximage->byte_order == LSBFirst) {
+ ximage->data[offset + 0] = b0;
+ ximage->data[offset + 1] = b1;
+ ximage->data[offset + 2] = b2;
+ ximage->data[offset + 3] = b3;
+ }
+ else { // MSBFirst
+ ximage->data[offset + 0] = b3;
+ ximage->data[offset + 1] = b2;
+ ximage->data[offset + 2] = b1;
+ ximage->data[offset + 3] = b0;
+ }
+ break;
+ default:
+ BX_PANIC(("X_graphics_tile_update: bits_per_pixel %u not implemented",
+ (unsigned) imBPP));
+ break;
+ }
+ }
+ }
+ }
+ XPutImage(bx_x_display, win, gc, ximage, 0, 0, x0, y0+bx_headerbar_y,
+ x_tilesize, y_tilesize);
+}
+
+
+ bx_bool
+bx_x_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue)
+{
+ // returns: 0=no screen update needed (color map change has direct effect)
+ // 1=screen updated needed (redraw using current colormap)
+ XColor color;
+
+ color.flags = DoRed | DoGreen | DoBlue;
+ color.red = red << 8;
+ color.green = green << 8;
+ color.blue = blue << 8;
+
+ if (bx_options.Oprivate_colormap->get ()) {
+ color.pixel = index;
+ XStoreColor(bx_x_display, default_cmap, &color);
+ return(0); // no screen update needed
+ }
+ else {
+ XAllocColor(bx_x_display, DefaultColormap(bx_x_display, bx_x_screen_num),
+ &color);
+ col_vals[index] = color.pixel;
+ return(1); // screen update needed
+ }
+}
+
+
+ void
+bx_x_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp)
+{
+ if ((bpp <= imBPP) && ((bpp == 8) || (bpp == 15) || (bpp == 16) || (bpp == 24) || (bpp == 32))) {
+ vga_bpp = bpp;
+ } else {
+ BX_PANIC(("%d bpp graphics mode not supported", bpp));
+ }
+ if (fheight > 0) {
+ font_height = fheight;
+ font_width = fwidth;
+ text_cols = x / font_width;
+ text_rows = y / font_height;
+ }
+ if ( (x != dimension_x) || (y != (dimension_y-bx_headerbar_y)) ) {
+ XSizeHints hints;
+ long supplied_return;
+
+ if ( XGetWMNormalHints(bx_x_display, win, &hints, &supplied_return) &&
+ supplied_return & PMaxSize ) {
+ hints.max_width = hints.min_width = x;
+ hints.max_height = hints.min_height = y+bx_headerbar_y;
+ XSetWMNormalHints(bx_x_display, win, &hints);
+ }
+ XResizeWindow(bx_x_display, win, x, y+bx_headerbar_y);
+ dimension_x = x;
+ dimension_y = y + bx_headerbar_y;
+ }
+}
+
+
+ void
+bx_x_gui_c::show_headerbar(void)
+{
+ unsigned xorigin;
+ int xleft, xright;
+
+ // clear header bar area to white
+ XFillRectangle(bx_x_display, win, gc_headerbar_inv, 0,0, dimension_x, bx_headerbar_y);
+
+ xleft = 0;
+ xright = dimension_x;
+ for (unsigned i=0; i<bx_headerbar_entries; i++) {
+ if (bx_headerbar_entry[i].alignment == BX_GRAVITY_LEFT) {
+ xorigin = bx_headerbar_entry[i].xorigin;
+ xleft += bx_headerbar_entry[i].xdim;
+ }
+ else {
+ xorigin = dimension_x - bx_headerbar_entry[i].xorigin;
+ xright = xorigin;
+ }
+ if (xright < xleft) break;
+ XCopyPlane(bx_x_display, bx_headerbar_entry[i].bitmap, win, gc_headerbar,
+ 0,0, bx_headerbar_entry[i].xdim, bx_headerbar_entry[i].ydim,
+ xorigin, 0, 1);
+ }
+}
+
+
+ unsigned
+bx_x_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim)
+{
+ if (bx_bitmap_entries >= BX_MAX_PIXMAPS) {
+ BX_PANIC(("x: too many pixmaps, increase BX_MAX_PIXMAPS"));
+ }
+
+ bx_bitmaps[bx_bitmap_entries].bmap =
+ XCreateBitmapFromData(bx_x_display, win, (const char *) bmap, xdim, ydim);
+ bx_bitmaps[bx_bitmap_entries].xdim = xdim;
+ bx_bitmaps[bx_bitmap_entries].ydim = ydim;
+ if (!bx_bitmaps[bx_bitmap_entries].bmap) {
+ BX_PANIC(("x: could not create bitmap"));
+ }
+ bx_bitmap_entries++;
+ return(bx_bitmap_entries-1); // return index as handle
+}
+
+
+ unsigned
+bx_x_gui_c::headerbar_bitmap(unsigned bmap_id, unsigned alignment, void (*f)(void))
+{
+ unsigned hb_index;
+
+ if ( (bx_headerbar_entries+1) > BX_MAX_HEADERBAR_ENTRIES )
+ BX_PANIC(("x: too many headerbar entries, increase BX_MAX_HEADERBAR_ENTRIES"));
+
+ bx_headerbar_entries++;
+ hb_index = bx_headerbar_entries - 1;
+
+ bx_headerbar_entry[hb_index].bitmap = bx_bitmaps[bmap_id].bmap;
+ bx_headerbar_entry[hb_index].xdim = bx_bitmaps[bmap_id].xdim;
+ bx_headerbar_entry[hb_index].ydim = bx_bitmaps[bmap_id].ydim;
+ bx_headerbar_entry[hb_index].alignment = alignment;
+ bx_headerbar_entry[hb_index].f = f;
+ if (alignment == BX_GRAVITY_LEFT) {
+ bx_headerbar_entry[hb_index].xorigin = bx_bitmap_left_xorigin;
+ bx_headerbar_entry[hb_index].yorigin = 0;
+ bx_bitmap_left_xorigin += bx_bitmaps[bmap_id].xdim;
+ }
+ else { // BX_GRAVITY_RIGHT
+ bx_bitmap_right_xorigin += bx_bitmaps[bmap_id].xdim;
+ bx_headerbar_entry[hb_index].xorigin = bx_bitmap_right_xorigin;
+ bx_headerbar_entry[hb_index].yorigin = 0;
+ }
+ return(hb_index);
+}
+
+ void
+bx_x_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id)
+{
+ unsigned xorigin;
+
+ bx_headerbar_entry[hbar_id].bitmap = bx_bitmaps[bmap_id].bmap;
+
+ if (bx_headerbar_entry[hbar_id].alignment == BX_GRAVITY_LEFT)
+ xorigin = bx_headerbar_entry[hbar_id].xorigin;
+ else
+ xorigin = dimension_x - bx_headerbar_entry[hbar_id].xorigin;
+ XCopyPlane(bx_x_display, bx_headerbar_entry[hbar_id].bitmap, win, gc_headerbar,
+ 0,0, bx_headerbar_entry[hbar_id].xdim, bx_headerbar_entry[hbar_id].ydim,
+ xorigin, 0, 1);
+}
+
+
+ void
+headerbar_click(int x, int y)
+{
+ int xorigin;
+
+ // assuming y is in bounds
+ UNUSED(y);
+ for (unsigned i=0; i<bx_headerbar_entries; i++) {
+ if (bx_headerbar_entry[i].alignment == BX_GRAVITY_LEFT)
+ xorigin = bx_headerbar_entry[i].xorigin;
+ else
+ xorigin = dimension_x - bx_headerbar_entry[i].xorigin;
+ if ( (x>=xorigin) && (x<(xorigin+int(bx_headerbar_entry[i].xdim))) ) {
+ bx_headerbar_entry[i].f();
+ return;
+ }
+ }
+}
+
+ void
+bx_x_gui_c::exit(void)
+{
+ if (!x_init_done) return;
+
+ // Delete the font bitmaps
+ for (int i=0; i<256; i++) {
+ //if (vgafont[i] != NULL)
+ XFreePixmap(bx_x_display,vgafont[i]);
+ }
+
+ if (bx_x_display)
+ XCloseDisplay (bx_x_display);
+ BX_INFO(("Exit."));
+}
+
+static void warp_cursor (int dx, int dy)
+{
+ if (bx_options.Omouse_enabled->get () &&
+ (warp_dx || warp_dy || dx || dy)
+ ) {
+ warp_dx = dx;
+ warp_dy = dy;
+ XWarpPointer(bx_x_display, None, None, 0, 0, 0, 0, dx, dy);
+ }
+}
+
+static void disable_cursor ()
+{
+ static Cursor cursor;
+ static unsigned cursor_created = 0;
+
+ static int shape_width = 16,
+ shape_height = 16,
+ mask_width = 16,
+ mask_height = 16;
+
+ static uint32 shape_bits[(16*16)/32] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ };
+ static uint32 mask_bits[(16*16)/32] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ };
+
+ if (!cursor_created) {
+ Pixmap shape, mask;
+ XColor white, black;
+ shape = XCreatePixmapFromBitmapData(bx_x_display,
+ RootWindow(bx_x_display,bx_x_screen_num),
+ (char*)shape_bits,
+ shape_width,
+ shape_height,
+ 1, 0, 1);
+ mask = XCreatePixmapFromBitmapData(bx_x_display,
+ RootWindow(bx_x_display,bx_x_screen_num),
+ (char*)mask_bits,
+ mask_width,
+ mask_height,
+ 1, 0, 1);
+ XParseColor(bx_x_display, default_cmap, "black", &black);
+ XParseColor(bx_x_display, default_cmap, "white", &white);
+ cursor = XCreatePixmapCursor(bx_x_display, shape, mask,
+ &white, &black, 1, 1);
+ cursor_created = 1;
+ }
+
+ XDefineCursor(bx_x_display, win, cursor);
+}
+
+static void enable_cursor ()
+{
+ XUndefineCursor(bx_x_display, win);
+}
+
+/* convertStringToXKeysym is a keymap callback
+ * used when reading the keymap file.
+ * It converts a Symblic String to a GUI Constant
+ *
+ * It returns a Bit32u constant or BX_KEYMAP_UNKNOWN if it fails
+ */
+static Bit32u convertStringToXKeysym (const char *string)
+{
+ if (strncmp ("XK_", string, 3) != 0)
+ return BX_KEYMAP_UNKNOWN;
+ KeySym keysym=XStringToKeysym(string+3);
+
+ // failure, return unknown
+ if(keysym==NoSymbol) return BX_KEYMAP_UNKNOWN;
+
+ return((Bit32u)keysym);
+}
+
+#if BX_USE_IDLE_HACK
+
+/* BX_USE_IDLE_HACK: a small idle hack by
+ * Roland.Mainz@informatik.med.uni-giessen.de to prevent bochs
+ * from consuming 100% CPU time even when it is not required (for
+ * example, the OS in the emulator calls HLT to wait for an interupt)
+ * pro:
+ * - no more 100% CPU usage
+ * contra:
+ * - we're sleeping too long
+ * - bochs still consumes ~10%-20% CPU time while executing an idle
+ * linux kernel
+ * - this is an hack
+ */
+
+/* XPeekEvent() with timeout
+ * (adopted from mozilla/gfx/src/xprint/xprintutil_printtofile.c#XNextEventTimeout())
+ */
+static
+Bool XPeekEventTimeout( Display *display, XEvent *event_return, struct timeval *timeout )
+{
+ int res;
+ fd_set readfds;
+ int display_fd = XConnectionNumber(display);
+
+ /* small shortcut... */
+ if( timeout == NULL )
+ {
+ XPeekEvent(display, event_return);
+ return(True);
+ }
+
+ FD_ZERO(&readfds);
+ FD_SET(display_fd, &readfds);
+
+ /* Note/bug: In the case of internal X events (like used to trigger callbacks
+ * registered by XpGetDocumentData()&co.) select() will return with "new info"
+ * - but XNextEvent() below processes these _internal_ events silently - and
+ * will block if there are no other non-internal events.
+ * The workaround here is to check with XEventsQueued() if there are non-internal
+ * events queued - if not select() will be called again - unfortunately we use
+ * the old timeout here instead of the "remaining" time... (this only would hurt
+ * if the timeout would be really long - but for current use with values below
+ * 1/2 secs it does not hurt... =:-)
+ */
+ while( XEventsQueued(display, QueuedAfterFlush) == 0 )
+ {
+ res = select(display_fd+1, &readfds, NULL, NULL, timeout);
+
+ switch(res)
+ {
+ case -1: /* select() error - should not happen */
+ perror("XPeekEventTimeout: select() failure");
+ return(False);
+ case 0: /* timeout */
+ return(False);
+ }
+ }
+
+ XPeekEvent(display, event_return);
+ return(True);
+}
+
+#if BX_USE_IDLE_HACK
+void bx_x_gui_c::sim_is_idle () {
+ XEvent dummy;
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 1000; /* 1/1000 s */
+ XPeekEventTimeout(bx_x_display, &dummy, &timeout);
+}
+#endif
+#endif /* BX_USE_IDLE_HACK */
+
+#endif /* if BX_WITH_X11 */
diff --git a/tools/ioemu/include/bochs.h b/tools/ioemu/include/bochs.h
new file mode 100644
index 0000000000..59b306702d
--- /dev/null
+++ b/tools/ioemu/include/bochs.h
@@ -0,0 +1,771 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: bochs.h,v 1.128.2.1 2004/02/06 22:14:25 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// bochs.h is the master header file for all C++ code. It includes all
+// the system header files needed by bochs, and also includes all the bochs
+// C++ header files. Because bochs.h and the files that it includes has
+// structure and class definitions, it cannot be called from C code.
+//
+
+#ifndef BX_BOCHS_H
+# define BX_BOCHS_H 1
+
+#include "config.h" /* generated by configure script from config.h.in */
+
+extern "C" {
+
+#ifdef WIN32
+// In a win32 compile (including cygwin), windows.h is required for several
+// files in gui and iodev. It is important to include it here in a header
+// file so that WIN32-specific data types can be used in fields of classes.
+#include <windows.h>
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#ifndef WIN32
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+#include <time.h>
+#if BX_WITH_MACOS
+#define Float32 KLUDGE_Float32
+#define Float64 KLUDGE_Float64
+# include <types.h>
+#undef Float32
+#undef Float64
+# include <stat.h>
+# include <cstdio>
+# include <unistd.h>
+#elif BX_WITH_CARBON
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sys/param.h> /* for MAXPATHLEN */
+# include <utime.h>
+#else
+# ifndef WIN32
+# include <sys/time.h>
+# endif
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#ifdef macintosh
+# define SuperDrive "[fd:]"
+#endif
+}
+
+#include "osdep.h" /* platform dependent includes and defines */
+#include "bxversion.h"
+
+#include "gui/siminterface.h"
+
+// prototypes
+int bx_begin_simulation (int argc, char *argv[]);
+
+//
+// some macros to interface the CPU and memory to external environment
+// so that these functions can be redirected to the debugger when
+// needed.
+//
+
+#if ((BX_DEBUGGER == 1) && (BX_NUM_SIMULATORS >= 2))
+// =-=-=-=-=-=-=- Redirected to cosimulation debugger -=-=-=-=-=-=-=
+#define DEV_vga_mem_read(addr) bx_dbg_ucmem_read(addr)
+#define DEV_vga_mem_write(addr, val) bx_dbg_ucmem_write(addr, val)
+
+#if BX_SUPPORT_A20
+# define A20ADDR(x) ( (x) & bx_pc_system.a20_mask )
+#else
+# define A20ADDR(x) (x)
+#endif
+#define BX_INP(addr, len) bx_dbg_inp(addr, len)
+#define BX_OUTP(addr, val, len) bx_dbg_outp(addr, val, len)
+#define BX_HRQ (bx_pc_system.HRQ)
+#define BX_RAISE_HLDA() bx_dbg_raise_HLDA()
+#define BX_TICK1()
+#define BX_INTR bx_pc_system.INTR
+#define BX_SET_INTR(b) bx_dbg_set_INTR(b)
+#if BX_SIM_ID == 0
+# define BX_CPU_C bx_cpu0_c
+# define BX_CPU bx_cpu0
+# define BX_MEM_C bx_mem0_c
+# define BX_MEM bx_mem0
+#else
+# define BX_CPU_C bx_cpu1_c
+# define BX_CPU bx_cpu1
+# define BX_MEM_C bx_mem1_c
+# define BX_MEM bx_mem1
+#endif
+#define BX_SET_ENABLE_A20(enabled) bx_dbg_async_pin_request(BX_DBG_ASYNC_PENDING_A20, \
+ enabled)
+#define BX_GET_ENABLE_A20() bx_pc_system.get_enable_a20()
+#error FIXME: cosim mode not fixed yet
+
+#else
+
+// =-=-=-=-=-=-=- Normal optimized use -=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#if BX_SUPPORT_A20
+# define A20ADDR(x) ( (x) & bx_pc_system.a20_mask )
+#else
+# define A20ADDR(x) (x)
+#endif
+//
+// some pc_systems functions just redirect to the IO devices so optimize
+// by eliminating call here
+//
+// #define BX_INP(addr, len) bx_pc_system.inp(addr, len)
+// #define BX_OUTP(addr, val, len) bx_pc_system.outp(addr, val, len)
+#define BX_INP(addr, len) bx_devices.inp(addr, len)
+#define BX_OUTP(addr, val, len) bx_devices.outp(addr, val, len)
+#define BX_TICK1() bx_pc_system.tick1()
+#define BX_TICKN(n) bx_pc_system.tickn(n)
+#define BX_INTR bx_pc_system.INTR
+#define BX_SET_INTR(b) bx_pc_system.set_INTR(b)
+#define BX_CPU_C bx_cpu_c
+#define BX_MEM_C bx_mem_c
+#define BX_HRQ (bx_pc_system.HRQ)
+#define BX_MEM_READ_PHYSICAL(phy_addr, len, ptr) \
+ BX_MEM(0)->readPhysicalPage(BX_CPU(0), phy_addr, len, ptr)
+#define BX_MEM_WRITE_PHYSICAL(phy_addr, len, ptr) \
+ BX_MEM(0)->writePhysicalPage(BX_CPU(0), phy_addr, len, ptr)
+
+#if BX_SMP_PROCESSORS==1
+#define BX_CPU(x) (&bx_cpu)
+#define BX_MEM(x) (&bx_mem)
+#else
+#define BX_CPU(x) (bx_cpu_array[x])
+#define BX_MEM(x) (bx_mem_array[x])
+#endif
+
+#define BX_SET_ENABLE_A20(enabled) bx_pc_system.set_enable_a20(enabled)
+#define BX_GET_ENABLE_A20() bx_pc_system.get_enable_a20()
+
+#endif
+
+
+// you can't use static member functions on the CPU, if there are going
+// to be 2 cpus. Check this early on.
+#if (BX_SMP_PROCESSORS>1)
+# if (BX_USE_CPU_SMF!=0)
+# error For SMP simulation, BX_USE_CPU_SMF must be 0.
+# endif
+#endif
+
+
+// #define BX_IAC() bx_pc_system.IAC()
+//#define BX_IAC() bx_dbg_IAC()
+
+//
+// Ways for the the external environment to report back information
+// to the debugger.
+//
+
+#if BX_DEBUGGER
+# define BX_DBG_ASYNC_INTR bx_guard.async.irq
+# define BX_DBG_ASYNC_DMA bx_guard.async.dma
+#if (BX_NUM_SIMULATORS > 1)
+// for multiple simulators, we always need this info, since we're
+// going to replay it.
+# define BX_DBG_DMA_REPORT(addr, len, what, val) \
+ bx_dbg_dma_report(addr, len, what, val)
+# define BX_DBG_IAC_REPORT(vector, irq) \
+ bx_dbg_iac_report(vector, irq)
+# define BX_DBG_A20_REPORT(val) \
+ bx_dbg_a20_report(val)
+# define BX_DBG_IO_REPORT(addr, size, op, val) \
+ bx_dbg_io_report(addr, size, op, val)
+# define BX_DBG_UCMEM_REPORT(addr, size, op, val)
+#else
+// for a single simulator debug environment, we can optimize a little
+// by conditionally calling, as per requested.
+
+# define BX_DBG_DMA_REPORT(addr, len, what, val) \
+ if (bx_guard.report.dma) bx_dbg_dma_report(addr, len, what, val)
+# define BX_DBG_IAC_REPORT(vector, irq) \
+ if (bx_guard.report.irq) bx_dbg_iac_report(vector, irq)
+# define BX_DBG_A20_REPORT(val) \
+ if (bx_guard.report.a20) bx_dbg_a20_report(val)
+# define BX_DBG_IO_REPORT(addr, size, op, val) \
+ if (bx_guard.report.io) bx_dbg_io_report(addr, size, op, val)
+# define BX_DBG_UCMEM_REPORT(addr, size, op, val) \
+ if (bx_guard.report.ucmem) bx_dbg_ucmem_report(addr, size, op, val)
+#endif // #if (BX_NUM_SIMULATORS > 1)
+
+#else // #if BX_DEBUGGER
+// debugger not compiled in, use empty stubs
+# define BX_DBG_ASYNC_INTR 1
+# define BX_DBG_ASYNC_DMA 1
+# define BX_DBG_DMA_REPORT(addr, len, what, val)
+# define BX_DBG_IAC_REPORT(vector, irq)
+# define BX_DBG_A20_REPORT(val)
+# define BX_DBG_IO_REPORT(addr, size, op, val)
+# define BX_DBG_UCMEM_REPORT(addr, size, op, val)
+#endif // #if BX_DEBUGGER
+
+#define MAGIC_LOGNUM 0x12345678
+
+typedef class BOCHSAPI logfunctions {
+ char *prefix;
+ int type;
+// values of onoff: 0=ignore, 1=report, 2=ask, 3=fatal
+#define ACT_IGNORE 0
+#define ACT_REPORT 1
+#define ACT_ASK 2
+#define ACT_FATAL 3
+#define N_ACT 4
+ int onoff[N_LOGLEV];
+ class iofunctions *logio;
+ // default log actions for all devices, declared and initialized
+ // in logio.cc.
+ BOCHSAPI_CYGONLY static int default_onoff[N_LOGLEV];
+public:
+ logfunctions(void);
+ logfunctions(class iofunctions *);
+ ~logfunctions(void);
+
+ void info(const char *fmt, ...) BX_CPP_AttrPrintf(2, 3);
+ void error(const char *fmt, ...) BX_CPP_AttrPrintf(2, 3);
+ void panic(const char *fmt, ...) BX_CPP_AttrPrintf(2, 3);
+ void pass(const char *fmt, ...) BX_CPP_AttrPrintf(2, 3);
+ void ldebug(const char *fmt, ...) BX_CPP_AttrPrintf(2, 3);
+ void fatal (const char *prefix, const char *fmt, va_list ap, int exit_status);
+#if BX_EXTERNAL_DEBUGGER
+ virtual void ask (int level, const char *prefix, const char *fmt, va_list ap);
+#else
+ void ask (int level, const char *prefix, const char *fmt, va_list ap);
+#endif
+ void put(char *);
+ void settype(int);
+ void setio(class iofunctions *);
+ void setonoff(int loglev, int value) {
+ assert (loglev >= 0 && loglev < N_LOGLEV);
+ onoff[loglev] = value;
+ }
+ char *getprefix () { return prefix; }
+ int getonoff(int level) {
+ assert (level>=0 && level<N_LOGLEV);
+ return onoff[level];
+ }
+ static void set_default_action (int loglev, int action) {
+ assert (loglev >= 0 && loglev < N_LOGLEV);
+ assert (action >= 0 && action < N_ACT);
+ default_onoff[loglev] = action;
+ }
+ static int get_default_action (int loglev) {
+ assert (loglev >= 0 && loglev < N_LOGLEV);
+ return default_onoff[loglev];
+ }
+} logfunc_t;
+
+#define BX_LOGPREFIX_SIZE 51
+
+enum {
+ IOLOG=0, FDLOG, GENLOG, CMOSLOG, CDLOG, DMALOG, ETHLOG, G2HLOG, HDLOG, KBDLOG,
+ NE2KLOG, PARLOG, PCILOG, PICLOG, PITLOG, SB16LOG, SERLOG, VGALOG,
+ STLOG, // state_file.cc
+ DEVLOG, MEMLOG, DISLOG, GUILOG, IOAPICLOG, APICLOG, CPU0LOG, CPU1LOG,
+ CPU2LOG, CPU3LOG, CPU4LOG, CPU5LOG, CPU6LOG, CPU7LOG, CPU8LOG, CPU9LOG,
+ CPU10LOG, CPU11LOG, CPU12LOG, CPU13LOG, CPU14LOG, CPU15LOG, CTRLLOG,
+ UNMAPLOG, SERRLOG, BIOSLOG, PIT81LOG, PIT82LOG, IODEBUGLOG, PCI2ISALOG,
+ PLUGINLOG, EXTFPUIRQLOG , PCIVGALOG, PCIUSBLOG, VTIMERLOG, STIMERLOG
+};
+
+class BOCHSAPI iofunctions {
+ int magic;
+ char logprefix[BX_LOGPREFIX_SIZE];
+ FILE *logfd;
+ class logfunctions *log;
+ void init(void);
+ void flush(void);
+
+// Log Class types
+public:
+ iofunctions(void);
+ iofunctions(FILE *);
+ iofunctions(int);
+ iofunctions(const char *);
+ ~iofunctions(void);
+
+ void out(int facility, int level, const char *pre, const char *fmt, va_list ap);
+
+ void init_log(const char *fn);
+ void init_log(int fd);
+ void init_log(FILE *fs);
+ void set_log_prefix(const char *prefix);
+ int get_n_logfns () { return n_logfn; }
+ logfunc_t *get_logfn (int index) { return logfn_list[index]; }
+ void add_logfn (logfunc_t *fn);
+ void set_log_action (int loglevel, int action);
+ const char *getlevel(int i) {
+ static const char *loglevel[N_LOGLEV] = {
+ "DEBUG",
+ "INFO",
+ "ERROR",
+ "PANIC",
+ "PASS"
+ };
+ if (i>=0 && i<N_LOGLEV) return loglevel[i];
+ else return "?";
+ }
+ char *getaction(int i) {
+ static char *name[] = { "ignore", "report", "ask", "fatal" };
+ assert (i>=ACT_IGNORE && i<N_ACT);
+ return name[i];
+ }
+
+protected:
+ int n_logfn;
+#define MAX_LOGFNS 128
+ logfunc_t *logfn_list[MAX_LOGFNS];
+ char *logfn;
+
+
+};
+
+typedef class BOCHSAPI iofunctions iofunc_t;
+
+
+#define SAFE_GET_IOFUNC() \
+ ((io==NULL)? (io=new iofunc_t("/dev/stderr")) : io)
+#define SAFE_GET_GENLOG() \
+ ((genlog==NULL)? (genlog=new logfunc_t(SAFE_GET_IOFUNC())) : genlog)
+/* #define NO_LOGGING */
+#ifndef NO_LOGGING
+
+#define BX_INFO(x) (LOG_THIS info) x
+#define BX_DEBUG(x) (LOG_THIS ldebug) x
+#define BX_ERROR(x) (LOG_THIS error) x
+#define BX_PANIC(x) (LOG_THIS panic) x
+#define BX_PASS(x) (LOG_THIS pass) x
+
+#else
+
+#define EMPTY do { } while(0)
+
+#define BX_INFO(x) EMPTY
+#define BX_DEBUG(x) EMPTY
+#define BX_ERROR(x) EMPTY
+#define BX_PANIC(x) (LOG_THIS panic) x
+#define BX_PASS(x) (LOG_THIS pass) x
+
+#endif
+
+BOCHSAPI extern iofunc_t *io;
+BOCHSAPI extern logfunc_t *genlog;
+
+#include "state_file.h"
+
+#ifndef UNUSED
+# define UNUSED(x) ((void)x)
+#endif
+
+#define uint8 Bit8u
+#define uint16 Bit16u
+#define uint32 Bit32u
+
+#ifdef BX_USE_VMX
+extern "C" {
+#include "xc.h"
+}
+
+extern void *shared_page;
+BOCHSAPI extern int xc_handle;
+#endif
+
+#if BX_PROVIDE_CPU_MEMORY==1
+# include "cpu/cpu.h"
+#endif
+
+#if BX_EXTERNAL_DEBUGGER
+# include "cpu/extdb.h"
+#endif
+
+#if BX_GDBSTUB
+// defines for GDB stub
+void bx_gdbstub_init(int argc, char* argv[]);
+int bx_gdbstub_check(unsigned int eip);
+#define GDBSTUB_STOP_NO_REASON (0xac0)
+
+#if BX_SMP_PROCESSORS!=1
+#error GDB stub was written for single processor support. If multiprocessor support is added, then we can remove this check.
+// The big problem is knowing which CPU gdb is referring to. In other words,
+// what should we put for "n" in BX_CPU(n)->dbg_xlate_linear2phy() and
+// BX_CPU(n)->dword.eip, etc.
+#endif
+
+#endif
+
+#if BX_DISASM
+# include "disasm/disasm.h"
+#endif
+
+typedef struct {
+ bx_bool floppy;
+ bx_bool keyboard;
+ bx_bool video;
+ bx_bool disk;
+ bx_bool pit;
+ bx_bool pic;
+ bx_bool bios;
+ bx_bool cmos;
+ bx_bool a20;
+ bx_bool interrupts;
+ bx_bool exceptions;
+ bx_bool unsupported;
+ bx_bool temp;
+ bx_bool reset;
+ bx_bool debugger;
+ bx_bool mouse;
+ bx_bool io;
+ bx_bool xms;
+ bx_bool v8086;
+ bx_bool paging;
+ bx_bool creg;
+ bx_bool dreg;
+ bx_bool dma;
+ bx_bool unsupported_io;
+ bx_bool serial;
+ bx_bool cdrom;
+#ifdef MAGIC_BREAKPOINT
+ bx_bool magic_break_enabled;
+#endif /* MAGIC_BREAKPOINT */
+#if BX_SUPPORT_APIC
+ bx_bool apic;
+ bx_bool ioapic;
+#endif
+#if BX_DEBUG_LINUX
+ bx_bool linux_syscall;
+#endif
+ void* record_io;
+ } bx_debug_t;
+
+#define BX_ASSERT(x) do {if (!(x)) BX_PANIC(("failed assertion \"%s\" at %s:%d\n", #x, __FILE__, __LINE__));} while (0)
+void bx_signal_handler (int signum);
+int bx_atexit(void);
+BOCHSAPI extern bx_debug_t bx_dbg;
+
+
+
+/* Already in gui/siminterface.h ???
+#define BX_FLOPPY_NONE 10 // floppy not present
+#define BX_FLOPPY_1_2 11 // 1.2M 5.25"
+#define BX_FLOPPY_1_44 12 // 1.44M 3.5"
+#define BX_FLOPPY_2_88 13 // 2.88M 3.5"
+#define BX_FLOPPY_720K 14 // 720K 3.5"
+#define BX_FLOPPY_360K 15 // 360K 5.25"
+#define BX_FLOPPY_LAST 15 // last one
+*/
+
+
+#define BX_READ 0
+#define BX_WRITE 1
+#define BX_RW 2
+
+
+
+
+
+#include "memory/memory.h"
+
+
+enum PCS_OP { PCS_CLEAR, PCS_SET, PCS_TOGGLE };
+
+#include "pc_system.h"
+#include "plugin.h"
+#include "gui/gui.h"
+#include "gui/textconfig.h"
+#include "gui/keymap.h"
+#include "iodev/iodev.h"
+
+
+
+
+
+
+
+/* --- EXTERNS --- */
+
+#if ( BX_PROVIDE_DEVICE_MODELS==1 )
+BOCHSAPI extern bx_devices_c bx_devices;
+#endif
+
+#if BX_GUI_SIGHANDLER
+extern bx_bool bx_gui_sighandler;
+#endif
+
+// This value controls how often each I/O device's periodic() method
+// gets called. The timer is set up in iodev/devices.cc.
+#define BX_IODEV_HANDLER_PERIOD 100 // microseconds
+//#define BX_IODEV_HANDLER_PERIOD 10 // microseconds
+
+char *bx_find_bochsrc (void);
+int bx_parse_cmdline (int arg, int argc, char *argv[]);
+int bx_read_configuration (char *rcfile);
+int bx_write_configuration (char *rcfile, int overwrite);
+void bx_reset_options (void);
+
+#define BX_PATHNAME_LEN 512
+
+typedef struct {
+ bx_param_bool_c *Opresent;
+ bx_param_num_c *Oioaddr1;
+ bx_param_num_c *Oioaddr2;
+ bx_param_num_c *Oirq;
+ } bx_ata_options;
+
+typedef struct {
+ bx_param_string_c *Opath;
+ bx_param_num_c *Oaddress;
+ } bx_rom_options;
+
+typedef struct {
+ bx_param_string_c *Opath;
+ } bx_vgarom_options;
+
+typedef struct {
+ bx_param_num_c *Osize;
+ } bx_mem_options;
+
+typedef struct {
+ bx_param_bool_c *Oenabled;
+ bx_param_string_c *Ooutfile;
+} bx_parport_options;
+
+typedef struct {
+ bx_param_string_c *Opath;
+ bx_param_bool_c *OcmosImage;
+ } bx_cmos_options;
+
+typedef struct {
+ bx_param_num_c *Otime0;
+ bx_param_enum_c *Osync;
+ } bx_clock_options;
+
+typedef struct {
+ bx_param_bool_c *Opresent;
+ bx_param_num_c *Oioaddr;
+ bx_param_num_c *Oirq;
+ bx_param_string_c *Omacaddr;
+ bx_param_enum_c *Oethmod;
+ bx_param_string_c *Oethdev;
+ bx_param_string_c *Oscript;
+ } bx_ne2k_options;
+
+typedef struct {
+// These options are used for a special hack to load a
+// 32bit OS directly into memory, so it can be run without
+// any of the 16bit real mode or BIOS assistance. This
+// is for the development of plex86, so we don't have
+// to implement real mode up front.
+ bx_param_num_c *OwhichOS;
+ bx_param_string_c *Opath;
+ bx_param_string_c *Oiolog;
+ bx_param_string_c *Oinitrd;
+ } bx_load32bitOSImage_t;
+
+typedef struct {
+ bx_param_string_c *Ofilename;
+ bx_param_string_c *Oprefix;
+ bx_param_string_c *Odebugger_filename;
+} bx_log_options;
+
+typedef struct {
+ bx_param_bool_c *Opresent;
+ bx_param_string_c *Omidifile;
+ bx_param_string_c *Owavefile;
+ bx_param_string_c *Ologfile;
+ bx_param_num_c *Omidimode;
+ bx_param_num_c *Owavemode;
+ bx_param_num_c *Ologlevel;
+ bx_param_num_c *Odmatimer;
+ } bx_sb16_options;
+
+typedef struct {
+ unsigned int port;
+ unsigned int text_base;
+ unsigned int data_base;
+ unsigned int bss_base;
+ } bx_gdbstub_t;
+
+typedef struct {
+ bx_param_bool_c *OuseMapping;
+ bx_param_string_c *Okeymap;
+ } bx_keyboard_options;
+
+#define BX_KBD_XT_TYPE 0
+#define BX_KBD_AT_TYPE 1
+#define BX_KBD_MF_TYPE 2
+
+#define BX_N_OPTROM_IMAGES 4
+#define BX_N_SERIAL_PORTS 1
+#define BX_N_PARALLEL_PORTS 1
+#define BX_N_USB_HUBS 1
+
+typedef struct BOCHSAPI {
+ bx_floppy_options floppya;
+ bx_floppy_options floppyb;
+ bx_ata_options ata[BX_MAX_ATA_CHANNEL];
+ bx_atadevice_options atadevice[BX_MAX_ATA_CHANNEL][2];
+ // bx_disk_options diskc;
+ // bx_disk_options diskd;
+ // bx_cdrom_options cdromd;
+ bx_serial_options com[BX_N_SERIAL_PORTS];
+ bx_usb_options usb[BX_N_USB_HUBS];
+ bx_rom_options rom;
+ bx_vgarom_options vgarom;
+ bx_rom_options optrom[BX_N_OPTROM_IMAGES]; // Optional rom images
+ bx_mem_options memory;
+ bx_parport_options par[BX_N_PARALLEL_PORTS]; // parallel ports
+ bx_sb16_options sb16;
+ bx_param_num_c *Obootdrive;
+ bx_param_bool_c *OfloppySigCheck;
+ bx_param_num_c *Ovga_update_interval;
+ bx_param_num_c *Okeyboard_serial_delay;
+ bx_param_num_c *Okeyboard_paste_delay;
+ bx_param_enum_c *Okeyboard_type;
+ bx_param_num_c *Ofloppy_command_delay;
+ bx_param_num_c *Oips;
+ bx_param_bool_c *Orealtime_pit;
+ bx_param_bool_c *Otext_snapshot_check;
+ bx_param_bool_c *Omouse_enabled;
+ bx_param_bool_c *Oprivate_colormap;
+#if BX_WITH_AMIGAOS
+ bx_param_bool_c *Ofullscreen;
+ bx_param_string_c *Oscreenmode;
+#endif
+ bx_param_bool_c *Oi440FXSupport;
+ bx_cmos_options cmos;
+ bx_clock_options clock;
+ bx_ne2k_options ne2k;
+ bx_param_bool_c *OnewHardDriveSupport;
+ bx_load32bitOSImage_t load32bitOSImage;
+ bx_log_options log;
+ bx_keyboard_options keyboard;
+ bx_param_string_c *Ouser_shortcut;
+ bx_gdbstub_t gdbstub;
+ bx_param_enum_c *Osel_config;
+ bx_param_enum_c *Osel_displaylib;
+ } bx_options_t;
+
+BOCHSAPI extern bx_options_t bx_options;
+
+void bx_center_print (FILE *file, char *line, int maxwidth);
+
+#if BX_PROVIDE_CPU_MEMORY==1
+#else
+// # include "external_interface.h"
+#endif
+
+#define BX_USE_PS2_MOUSE 1
+
+int bx_init_hardware ();
+
+#include "instrument.h"
+
+// These are some convenience macros which abstract out accesses between
+// a variable in native byte ordering to/from guest (x86) memory, which is
+// always in little endian format. You must deal with alignment (if your
+// system cares) and endian rearranging. Don't assume anything. You could
+// put some platform specific asm() statements here, to make use of native
+// instructions to help perform these operations more efficiently than C++.
+
+
+#ifdef __i386__
+
+#define WriteHostWordToLittleEndian(hostPtr, nativeVar16) \
+ *((Bit16u*)(hostPtr)) = (nativeVar16)
+#define WriteHostDWordToLittleEndian(hostPtr, nativeVar32) \
+ *((Bit32u*)(hostPtr)) = (nativeVar32)
+#define WriteHostQWordToLittleEndian(hostPtr, nativeVar64) \
+ *((Bit64u*)(hostPtr)) = (nativeVar64)
+#define ReadHostWordFromLittleEndian(hostPtr, nativeVar16) \
+ (nativeVar16) = *((Bit16u*)(hostPtr))
+#define ReadHostDWordFromLittleEndian(hostPtr, nativeVar32) \
+ (nativeVar32) = *((Bit32u*)(hostPtr))
+#define ReadHostQWordFromLittleEndian(hostPtr, nativeVar64) \
+ (nativeVar64) = *((Bit64u*)(hostPtr))
+
+#else
+
+#define WriteHostWordToLittleEndian(hostPtr, nativeVar16) { \
+ ((Bit8u *)(hostPtr))[0] = (Bit8u) (nativeVar16); \
+ ((Bit8u *)(hostPtr))[1] = (Bit8u) ((nativeVar16)>>8); \
+ }
+#define WriteHostDWordToLittleEndian(hostPtr, nativeVar32) { \
+ ((Bit8u *)(hostPtr))[0] = (Bit8u) (nativeVar32); \
+ ((Bit8u *)(hostPtr))[1] = (Bit8u) ((nativeVar32)>>8); \
+ ((Bit8u *)(hostPtr))[2] = (Bit8u) ((nativeVar32)>>16); \
+ ((Bit8u *)(hostPtr))[3] = (Bit8u) ((nativeVar32)>>24); \
+ }
+#define WriteHostQWordToLittleEndian(hostPtr, nativeVar64) { \
+ ((Bit8u *)(hostPtr))[0] = (Bit8u) (nativeVar64); \
+ ((Bit8u *)(hostPtr))[1] = (Bit8u) ((nativeVar64)>>8); \
+ ((Bit8u *)(hostPtr))[2] = (Bit8u) ((nativeVar64)>>16); \
+ ((Bit8u *)(hostPtr))[3] = (Bit8u) ((nativeVar64)>>24); \
+ ((Bit8u *)(hostPtr))[4] = (Bit8u) ((nativeVar64)>>32); \
+ ((Bit8u *)(hostPtr))[5] = (Bit8u) ((nativeVar64)>>40); \
+ ((Bit8u *)(hostPtr))[6] = (Bit8u) ((nativeVar64)>>48); \
+ ((Bit8u *)(hostPtr))[7] = (Bit8u) ((nativeVar64)>>56); \
+ }
+#define ReadHostWordFromLittleEndian(hostPtr, nativeVar16) { \
+ (nativeVar16) = ((Bit16u) ((Bit8u *)(hostPtr))[0]) | \
+ (((Bit16u) ((Bit8u *)(hostPtr))[1])<<8) ; \
+ }
+#define ReadHostDWordFromLittleEndian(hostPtr, nativeVar32) { \
+ (nativeVar32) = ((Bit32u) ((Bit8u *)(hostPtr))[0]) | \
+ (((Bit32u) ((Bit8u *)(hostPtr))[1])<<8) | \
+ (((Bit32u) ((Bit8u *)(hostPtr))[2])<<16) | \
+ (((Bit32u) ((Bit8u *)(hostPtr))[3])<<24); \
+ }
+#define ReadHostQWordFromLittleEndian(hostPtr, nativeVar64) { \
+ (nativeVar64) = ((Bit64u) ((Bit8u *)(hostPtr))[0]) | \
+ (((Bit64u) ((Bit8u *)(hostPtr))[1])<<8) | \
+ (((Bit64u) ((Bit8u *)(hostPtr))[2])<<16) | \
+ (((Bit64u) ((Bit8u *)(hostPtr))[3])<<24) | \
+ (((Bit64u) ((Bit8u *)(hostPtr))[4])<<32) | \
+ (((Bit64u) ((Bit8u *)(hostPtr))[5])<<40) | \
+ (((Bit64u) ((Bit8u *)(hostPtr))[6])<<48) | \
+ (((Bit64u) ((Bit8u *)(hostPtr))[7])<<56); \
+ }
+
+#endif
+
+#ifdef BX_USE_VMX
+extern int domid;
+extern unsigned long megabytes;
+#endif
+
+#endif /* BX_BOCHS_H */
diff --git a/tools/ioemu/include/bxversion.h b/tools/ioemu/include/bxversion.h
new file mode 100644
index 0000000000..1c640f9f86
--- /dev/null
+++ b/tools/ioemu/include/bxversion.h
@@ -0,0 +1,7 @@
+/////////////////////////////////////////////////////////////////////////
+// This file is checked in as bxversion.h.in. The configure script
+// substitutes variables and creates bxversion.h.
+/////////////////////////////////////////////////////////////////////////
+
+#define VER_STRING "2.1.1"
+#define REL_STRING "February 08, 2004"
diff --git a/tools/ioemu/include/config.h b/tools/ioemu/include/config.h
new file mode 100644
index 0000000000..e48f2d97e5
--- /dev/null
+++ b/tools/ioemu/include/config.h
@@ -0,0 +1,919 @@
+/* config.h. Generated by configure. */
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// config.h.in is distributed in the source TAR file. When you run
+// the configure script, it generates config.h with some changes
+// according to your build environment. For example, in config.h.in,
+// SIZEOF_UNSIGNED_CHAR is set to 0. When configure produces config.h
+// it will change "0" to the detected value for your system.
+//
+// config.h contains ONLY preprocessor #defines and a few typedefs.
+// It must be included by both C and C++ files, so it must not
+// contain anything language dependent such as a class declaration.
+//
+
+#ifdef _BX_CONFIG_H_
+#else
+#define _BX_CONFIG_H_ 1
+
+///////////////////////////////////////////////////////////////////
+// USER CONFIGURABLE OPTIONS : EDIT ONLY OPTIONS IN THIS SECTION //
+///////////////////////////////////////////////////////////////////
+
+
+#if 1
+// quit_sim is defined in gui/siminterface.h
+#define BX_EXIT(x) SIM->quit_sim (x)
+#else
+// provide the real main and the usual exit.
+#define BX_EXIT(x) ::exit(x)
+#endif
+
+#define BX_USE_CONFIG_INTERFACE 0
+#define BX_USE_VMX 1
+
+// I have tested the following combinations:
+// * processors=1, bootstrap=0, ioapic_id=1 (uniprocessor system)
+// * processors=2, bootstrap=0, ioapic_id=2
+// * processors=4, bootstrap=2, ioapic_id=4
+#define BX_SMP_PROCESSORS 1
+#define BX_BOOTSTRAP_PROCESSOR 0
+// choose IOAPIC id to be equal to the number of processors. This leaves
+// one space for each processor to have an ID, starting with 0.
+#define BX_IOAPIC_DEFAULT_ID 1
+
+#define BX_ADDRESS_SPACES 1
+// controls how many instances of BX_MEM_C are created. For
+// SMP, use several processors with one shared memory space.
+// For cosimulation, you could use two processors and two address
+// spaces.
+
+#define BX_SUPPORT_APIC 0
+// include in APIC models, required for a multiprocessor system.
+
+#if (BX_SMP_PROCESSORS>1 && !BX_SUPPORT_APIC)
+#error For multiprocessor simulation, BX_SUPPORT_APIC is required.
+#endif
+
+#define BX_DEBUG_LINUX 0
+// if simulating Linux, this provides a few more debugging options
+// such as tracing all system calls.
+
+#define HAVE_LIBREADLINE 0
+#define HAVE_READLINE_HISTORY_H 1
+// adds support for the GNU readline library in the debugger command
+// prompt.
+
+#define HAVE_LOCALE_H 0
+// Define to 1 if you have <locale.h>
+
+// I rebuilt the code which provides timers to IO devices.
+// Setting this to 1 will introduce a little code which
+// will panic out if cases which shouldn't happen occur.
+// Set this to 0 for optimal performance.
+#define BX_TIMER_DEBUG 1
+
+// Settable A20 line. For efficiency, you can disable
+// having a settable A20 line, eliminating conditional
+// code for every physical memory access. You'll have
+// to tell your software not to mess with the A20 line,
+// and accept it as always being on if you change this.
+// 1 = use settable A20 line. (normal)
+// 0 = A20 is like the rest of the address lines
+
+#define BX_SUPPORT_A20 1
+
+// Processor Instructions Per Second
+// To find out what value to use for the 'ips' directive
+// in your '.bochsrc' file, set BX_SHOW_IPS to 1, and
+// run the software in bochs you plan to use most. Bochs
+// will print out periodic IPS ratings. This will change
+// based on the processor mode at the time, and various
+// other factors. You'll get a reasonable estimate though.
+// When you're done, reset BX_SHOW_IPS to 0, do a
+// 'make all-clean', then 'make' again.
+
+#define BX_SHOW_IPS 0
+
+
+#if (BX_SHOW_IPS) && defined(__MINGW32__)
+#define SIGALRM 14
+#endif
+
+// Paging Options:
+// ---------------
+// Support Paging mechanism.
+// 0 = don't support paging at all (DOS & Minix don't require it)
+// 1 = support paging. (Most other OS's require paging)
+// Use Translation Lookaside Buffer (TLB) for caching
+// paging translations. This will make paging mode
+// more efficient. If you're OS doesn't use paging,
+// then you won't need either.
+// 1 = Use a TLB for effiency
+// 0 = don't use a TLB, walk the page tables for every access
+// BX_TLB_SIZE: Number of entries in TLB
+// BX_TLB_INDEX_OF(lpf): This macro is passed the linear page frame
+// (top 20 bits of the linear address. It must map these bits to
+// one of the TLB cache slots, given the size of BX_TLB_SIZE.
+// There will be a many-to-one mapping to each TLB cache slot.
+// When there are collisions, the old entry is overwritten with
+// one for the newest access.
+
+#define BX_SUPPORT_PAGING 1
+#define BX_USE_TLB 1
+
+#define BX_TLB_SIZE 1024
+#define BX_TLB_INDEX_OF(lpf) (((lpf) & 0x003ff000) >> 12)
+
+#define BX_USE_QUICK_TLB_INVALIDATE 0
+
+// Compile in support for DMA & FLOPPY IO. You'll need this
+// if you plan to use the floppy drive emulation. But if
+// you're environment doesn't require it, you can change
+// it to 0.
+
+#define BX_DMA_FLOPPY_IO 1
+
+
+// Default number of Megs of memory to emulate. The
+// 'megs:' directive in the '.bochsrc' file overrides this,
+// allowing per-run settings.
+
+#define BX_DEFAULT_MEM_MEGS 4
+
+
+// CPU level emulation. Default level is set in
+// the configure script. BX_CPU_LEVEL defines the CPU level
+// to emulate. BX_CPU_LEVEL_HACKED is a hack to define the
+// level of some integer instructions, so they can be tested
+// before the rest of the emulation is up to that level.
+
+#define BX_CPU_LEVEL 5
+#define BX_CPU_LEVEL_HACKED 5
+
+// emulate x86-64 instruction set?
+#define BX_SUPPORT_X86_64 0
+
+// Virtual 8086 mode emulation.
+// 1 = compile in support for v8086 mode.
+// 0 = don't compile in support for v8086 mode.
+#define BX_SUPPORT_V8086_MODE 1
+
+// Defines if the simulation should reset on triple fault
+// if set to 0, the simulation will panic.
+#define BX_RESET_ON_TRIPLE_FAULT 1
+
+// Support shadowing of ROM from C0000 to FFFFF.
+// This allows that region to be written to.
+#define BX_SHADOW_RAM 0
+
+// Number of CMOS registers
+#define BX_NUM_CMOS_REGS 64
+//#define BX_NUM_CMOS_REGS 128
+
+// Use Greg Alexander's new PIT model (summer 2001) instead of the original.
+#define BX_USE_NEW_PIT 1
+
+#define BX_USE_SLOWDOWN_TIMER 0
+#define BX_HAVE_SLEEP 1
+#define BX_HAVE_MSLEEP 0
+#define BX_HAVE_USLEEP 1
+#define BX_HAVE_NANOSLEEP 1
+#define BX_HAVE_ABORT 1
+#define BX_HAVE_SOCKLEN_T 1
+#define BX_HAVE_SOCKADDR_IN_SIN_LEN 0
+#define BX_HAVE_GETTIMEOFDAY 1
+#if defined(WIN32)
+#define BX_HAVE_REALTIME_USEC 1
+#else
+#define BX_HAVE_REALTIME_USEC (BX_HAVE_GETTIMEOFDAY)
+#endif
+#define BX_HAVE_MKSTEMP 1
+#define BX_HAVE_SYS_MMAN_H 1
+#define BX_HAVE_XPM_H 1
+#define BX_HAVE_TIMELOCAL 1
+#define BX_HAVE_GMTIME 1
+#define BX_HAVE_MKTIME 1
+
+// This turns on Roland Mainz's idle hack. Presently it is specific to the X11
+// gui. If people try to enable it elsewhere, give a compile error after the
+// gui definition so that they don't waste their time trying.
+#define BX_USE_IDLE_HACK 0
+
+// Minimum Emulated IPS.
+// This is used in the realtime PIT as well as for checking the
+// IPS value set in the config file.
+#define BX_MIN_IPS 200000
+
+// Use Static Member Funtions to eliminate 'this' pointer passing
+// If you want the efficiency of 'C', you can make all the
+// members of the C++ CPU class to be static.
+// This defaults to 1 since it should improve performance, but when
+// SMP mode is enabled, it will be turned off by configure.
+
+#define BX_USE_CPU_SMF 1
+
+// Use static member functions in IO DEVice emulation modules.
+// For efficiency, use C like functions for IO handling,
+// and declare a device instance at compile time,
+// instead of using 'new' and storing the pointer. This
+// eliminates some overhead, especially for high-use IO
+// devices like the disk drive.
+// 1 = Use static member efficiency (normal)
+// 0 = Use nonstatic member functions (use only if you need
+// multiple instances of a device class
+
+#define BX_USE_HD_SMF 1 // Hard drive
+#define BX_USE_BIOS_SMF 1 // BIOS
+#define BX_USE_CMOS_SMF 1 // CMOS
+#define BX_USE_DMA_SMF 1 // DMA
+#define BX_USE_FD_SMF 1 // Floppy
+#define BX_USE_KEY_SMF 1 // Keyboard
+#define BX_USE_PAR_SMF 1 // Parallel
+#define BX_USE_PIC_SMF 1 // PIC
+#define BX_USE_PIT_SMF 1 // PIT
+#define BX_USE_SER_SMF 1 // Serial
+#define BX_USE_UM_SMF 1 // Unmapped
+#define BX_USE_VGA_SMF 1 // VGA
+#define BX_USE_SB16_SMF 1 // Sound (SB 16)
+#define BX_USE_DEV_SMF 1 // System Devices (port92)
+#define BX_USE_PCI_SMF 1 // PCI
+#define BX_USE_P2I_SMF 1 // PCI-to-ISA bridge
+#define BX_USE_PCIVGA_SMF 1 // PCI-VGA
+#define BX_USE_PCIUSB_SMF 1 // USB hub
+#define BX_USE_NE2K_SMF 1 // NE2K
+#define BX_USE_EFI_SMF 1 // External FPU IRQ
+#define BX_USE_GAME_SMF 1 // Gameport
+
+#define BX_PLUGINS 0
+#define BX_HAVE_DLFCN_H 1
+
+#if BX_PLUGINS && \
+ ( !BX_USE_HD_SMF || !BX_USE_BIOS_SMF || !BX_USE_CMOS_SMF \
+ || !BX_USE_DMA_SMF || !BX_USE_FD_SMF || !BX_USE_KEY_SMF \
+ || !BX_USE_PAR_SMF || !BX_USE_PIC_SMF || !BX_USE_PIT_SMF \
+ || !BX_USE_SER_SMF || !BX_USE_UM_SMF || !BX_USE_VGA_SMF \
+ || !BX_USE_SB16_SMF || !BX_USE_DEV_SMF || !BX_USE_PCI_SMF \
+ || !BX_USE_P2I_SMF || !BX_USE_PCIVGA_SMF || !BX_USE_PCIUSB_SMF \
+ || !BX_USE_NE2K_SMF || !BX_USE_EFI_SMF || !BX_USE_GAME_SMF)
+#error You must use SMF to have plugins
+#endif
+
+#define BX_SUPPORT_SB16 0
+#define BX_SUPPORT_GAME 0
+
+#if BX_SUPPORT_SB16
+// Use virtual methods for the sound output functions
+#define BX_USE_SOUND_VIRTUAL 1
+// Determines which sound output class is to be used.
+// Currently the following are available:
+// bx_sound_linux_c Output for Linux, to /dev/dsp and /dev/midi00
+// bx_sound_windows_c Output for Windows midi and wave mappers
+// bx_sound_output_c Dummy functions, no output
+#define BX_SOUND_OUTPUT_C bx_sound_output_c
+#endif
+
+#define USE_RAW_SERIAL 0
+
+#define BX_USE_SPECIFIED_TIME0 0
+
+// This enables writing to port 0xe9 and the output
+// is sent to the console. Reading from port 0xe9
+// will return 0xe9 to let you know this is available.
+// Leave this 0 unless you have a reason to use it.
+#define BX_PORT_E9_HACK 1
+
+#define BX_GDBSTUB 0
+
+// This option enables compressed (zlib) hd support
+#define BX_COMPRESSED_HD_SUPPORT 0
+#define BX_HAVE_ZLIB 1
+
+#if BX_COMPRESSED_HD_SUPPORT && !BX_HAVE_ZLIB
+#error You must have zlib to enable compressed hd support
+#endif
+
+// This option defines the number of supported ATA channels.
+// There are up to two drives per ATA channel.
+#define BX_MAX_ATA_CHANNEL 4
+
+#if (BX_MAX_ATA_CHANNEL>4 || BX_MAX_ATA_CHANNEL<1)
+# error "BX_MAX_ATA_CHANNEL should be between 1 and 4"
+#endif
+
+// =================================================================
+// BEGIN: OPTIONAL DEBUGGER SECTION
+//
+// These options are only used if you compile in support for the
+// native command line debugging environment. Typically, the debugger
+// is not used, and this section can be ignored.
+// =================================================================
+
+#define BX_MAX_DIRTY_PAGE_TABLE_MEGS 1024
+
+// Compile in support for virtual/linear/physical breakpoints.
+// Set to 1, only those you need. Recommend using only linear
+// breakpoints, unless you need others. Less supported means
+// slightly faster execution time.
+#define BX_DBG_SUPPORT_VIR_BPOINT 1
+#define BX_DBG_SUPPORT_LIN_BPOINT 1
+#define BX_DBG_SUPPORT_PHY_BPOINT 1
+
+// You need only define one initial breakpoint into each
+// cpu simulator (emulator) here. Each simulator sets callbacks
+// and variables which the debugger uses from then on.
+#define BX_SIM1_INIT bx_dbg_init_cpu_mem_env0
+#ifndef BX_SIM2_INIT
+#define BX_SIM2_INIT bx_dbg_init_cpu_mem_env1
+#endif
+//#define BX_SIM2_INIT sim2_init
+
+// max number of virtual/linear/physical breakpoints handled
+#define BX_DBG_MAX_VIR_BPOINTS 10
+#define BX_DBG_MAX_LIN_BPOINTS 10
+#define BX_DBG_MAX_PHY_BPOINTS 10
+
+// max file pathname size for debugger commands
+#define BX_MAX_PATH 256
+// max nesting level for debug scripts including other scripts
+#define BX_INFILE_DEPTH 10
+// use this command to include (nest) debug scripts
+#define BX_INCLUDE_CMD "source"
+
+// Use either 32 or 64 bit instruction counter for
+// debugger purposes. Uncomment one of these.
+//#define BX_DBG_ICOUNT_SIZE 32
+#define BX_DBG_ICOUNT_SIZE 64
+
+// Make a call to command line debugger extensions. If set to 1,
+// a call is made. An external routine has a chance to process
+// the command. If it does, than the debugger ignores the command.
+#define BX_DBG_EXTENSIONS 0
+
+// =================================================================
+// END: OPTIONAL DEBUGGER SECTION
+// =================================================================
+
+
+//////////////////////////////////////////////////////////////////////
+// END OF USER CONFIGURABLE OPTIONS : DON'T EDIT ANYTHING BELOW !!! //
+// THIS IS GENERATED BY THE ./configure SCRIPT //
+//////////////////////////////////////////////////////////////////////
+
+
+#define BX_WITH_X11 1
+#define BX_WITH_BEOS 0
+#define BX_WITH_WIN32 0
+#define BX_WITH_MACOS 0
+#define BX_WITH_CARBON 0
+#define BX_WITH_NOGUI 0
+#define BX_WITH_TERM 0
+#define BX_WITH_RFB 0
+#define BX_WITH_AMIGAOS 0
+#define BX_WITH_SDL 0
+#define BX_WITH_SVGA 0
+#define BX_WITH_WX 0
+
+// add special export symbols for win32 DLL building. The main code must
+// have __declspec(dllexport) on variables, functions, or classes that the
+// plugins can access. The plugins should #define PLUGGABLE which will
+// activate the __declspec(dllimport) instead.
+#if defined(WIN32) || defined(__CYGWIN__)
+# if BX_PLUGINS && defined(BX_PLUGGABLE)
+// #warning I will import DLL symbols from Bochs main program.
+# define BOCHSAPI __declspec(dllimport)
+# else
+// #warning I will export DLL symbols.
+# define BOCHSAPI __declspec(dllexport)
+# endif
+#endif
+#ifndef BOCHSAPI
+# define BOCHSAPI
+#endif
+
+#if defined(__CYGWIN__)
+// Make BOCHSAPI_CYGONLY exactly the same as BOCHSAPI. This symbol
+// will be used for any cases where Cygwin requires a special tag
+// but VC++ does not.
+#define BOCHSAPI_CYGONLY BOCHSAPI
+#else
+// define the symbol to be empty
+#define BOCHSAPI_CYGONLY /*empty*/
+#endif
+
+#define BX_DEFAULT_CONFIG_INTERFACE "defined_by_configure"
+#define BX_DEFAULT_DISPLAY_LIBRARY "defined_by_configure"
+
+// Roland Mainz's idle hack is presently specific to X11. If people try to
+// enable it elsewhere, give a compile error so that they don't waste their
+// time trying.
+#if (BX_USE_IDLE_HACK && !BX_WITH_X11)
+# error IDLE_HACK will only work with the X11 gui. Correct configure args and retry.
+#endif
+
+#define WORDS_BIGENDIAN 0
+
+#define SIZEOF_UNSIGNED_CHAR 1
+#define SIZEOF_UNSIGNED_SHORT 2
+#define SIZEOF_UNSIGNED_INT 4
+#define SIZEOF_UNSIGNED_LONG 4
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+#define SIZEOF_INT_P 4
+
+#define BX_64BIT_CONSTANTS_USE_LL 1
+#if BX_64BIT_CONSTANTS_USE_LL
+// doesn't work on Microsoft Visual C++, maybe others
+#define BX_CONST64(x) (x##LL)
+#else
+#define BX_CONST64(x) (x)
+#endif
+
+#if defined(WIN32)
+ typedef unsigned char Bit8u;
+ typedef signed char Bit8s;
+ typedef unsigned short Bit16u;
+ typedef signed short Bit16s;
+ typedef unsigned int Bit32u;
+ typedef signed int Bit32s;
+#ifdef __MINGW32__
+ typedef unsigned long long Bit64u;
+ typedef signed long long Bit64s;
+#include <sys/types.h>
+#else
+ typedef unsigned __int64 Bit64u;
+ typedef signed __int64 Bit64s;
+#endif
+#elif BX_WITH_MACOS
+ typedef unsigned char Bit8u;
+ typedef signed char Bit8s;
+ typedef unsigned short Bit16u;
+ typedef signed short Bit16s;
+ typedef unsigned int Bit32u;
+ typedef signed int Bit32s;
+ typedef unsigned long long Bit64u;
+ typedef signed long long Bit64s;
+#else
+
+// Unix like platforms
+
+#if SIZEOF_UNSIGNED_CHAR != 1
+# error "sizeof (unsigned char) != 1"
+#else
+ typedef unsigned char Bit8u;
+ typedef signed char Bit8s;
+#endif
+
+#if SIZEOF_UNSIGNED_SHORT != 2
+# error "sizeof (unsigned short) != 2"
+#else
+ typedef unsigned short Bit16u;
+ typedef signed short Bit16s;
+#endif
+
+#if SIZEOF_UNSIGNED_INT == 4
+ typedef unsigned int Bit32u;
+ typedef signed int Bit32s;
+#elif SIZEOF_UNSIGNED_LONG == 4
+ typedef unsigned long Bit32u;
+ typedef signed long Bit32s;
+#else
+# error "can't find sizeof(type) of 4 bytes!"
+#endif
+
+#if SIZEOF_UNSIGNED_LONG == 8
+ typedef unsigned long Bit64u;
+ typedef signed long Bit64s;
+#elif SIZEOF_UNSIGNED_LONG_LONG == 8
+ typedef unsigned long long Bit64u;
+ typedef signed long long Bit64s;
+#else
+# error "can't find data type of 8 bytes"
+#endif
+
+#endif
+
+// now that Bit32u and Bit64u exist, defined bx_address
+#if BX_SUPPORT_X86_64
+typedef Bit64u bx_address;
+#else
+typedef Bit32u bx_address;
+#endif
+
+
+// technically, in an 8 bit signed the real minimum is -128, not -127.
+// But if you decide to negate -128 you tend to get -128 again, so it's
+// better not to use the absolute maximum in the signed range.
+#define BX_MAX_BIT64U ( (Bit64u) -1 )
+#define BX_MIN_BIT64U ( 0 )
+#define BX_MAX_BIT64S ( ((Bit64u) -1) >> 1 )
+#define BX_MIN_BIT64S ( -(((Bit64u) -1) >> 1) )
+#define BX_MAX_BIT32U ( (Bit32u) -1 )
+#define BX_MIN_BIT32U ( 0 )
+#define BX_MAX_BIT32S ( ((Bit32u) -1) >> 1 )
+#define BX_MIN_BIT32S ( -(((Bit32u) -1) >> 1) )
+#define BX_MAX_BIT16U ( (Bit16u) -1 )
+#define BX_MIN_BIT16U ( 0 )
+#define BX_MAX_BIT16S ( ((Bit16u) -1) >> 1 )
+#define BX_MIN_BIT16S ( -(((Bit16u) -1) >> 1) )
+#define BX_MAX_BIT8U ( (Bit8u) -1 )
+#define BX_MIN_BIT8U ( 0 )
+#define BX_MAX_BIT8S ( ((Bit8u) -1) >> 1 )
+#define BX_MIN_BIT8S ( -(((Bit8u) -1) >> 1) )
+
+
+// create an unsigned integer type that is the same size as a pointer.
+// You can typecast a pointer to a bx_pr_equiv_t without losing any
+// bits (and without getting the compiler excited). This is used in
+// the FPU emulation code, where pointers and integers are often
+// used interchangeably.
+#if SIZEOF_INT_P == 4
+ typedef Bit32u bx_ptr_equiv_t;
+#elif SIZEOF_INT_P == 8
+ typedef Bit64u bx_ptr_equiv_t;
+#else
+# error "could not define bx_ptr_equiv_t to size of int*"
+#endif
+
+// Use a boolean type that will not conflict with the builtin type
+// on any system.
+typedef Bit32u bx_bool;
+
+#if BX_WITH_MACOS
+# define bx_ptr_t char *
+#else
+# define bx_ptr_t void *
+#endif
+
+#if defined(WIN32)
+# define BX_LITTLE_ENDIAN
+#elif BX_WITH_MACOS
+# define BX_BIG_ENDIAN
+#else
+#if WORDS_BIGENDIAN
+# define BX_BIG_ENDIAN
+#else
+# define BX_LITTLE_ENDIAN
+#endif
+#endif // defined(WIN32)
+
+
+#if BX_SUPPORT_X86_64
+#ifdef BX_LITTLE_ENDIAN
+typedef
+ struct {
+ Bit64u lo;
+ Bit64u hi;
+ } Bit128u;
+typedef
+ struct {
+ Bit64u lo;
+ Bit64s hi;
+ } Bit128s;
+#else // must be Big Endian
+typedef
+ struct {
+ Bit64u hi;
+ Bit64u lo;
+ } Bit128u;
+typedef
+ struct {
+ Bit64s hi;
+ Bit64u lo;
+ } Bit128s;
+#endif
+#endif // #if BX_SUPPORT_X86_64
+
+
+// for now only term.cc requires a GUI sighandler.
+#define BX_GUI_SIGHANDLER (BX_WITH_TERM)
+
+#define HAVE_SIGACTION 1
+
+// configure will change the definition of "inline" to the value
+// that the C compiler allows. It tests the following keywords to
+// see if any is permitted: inline, __inline__, __inline. If none
+// is permitted, it defines inline to be empty.
+#define inline inline
+
+// inline functions in headers that are compiled with C compiler
+// (e.g. fpu code) are declared with BX_C_INLINE macro. Note that
+// the word "inline" itself may now be redefined by the above #define.
+// Many compilers are known to work with "static inline". If the
+// compiler can put the function inline, it does so and never creates
+// a symbol for the function. If optimization is off, or inline is
+// defined to be empty, the static keyword causes the function to create
+// a symbol that's visible only to that .c file. Each .c file that
+// includes the header will produde another local version of the
+// BX_C_INLINE function (not ideal). However without "static" you can
+// duplicate symbol problems which are even worse.
+#define BX_C_INLINE static inline
+
+// Use BX_CPP_INLINE for all C++ inline functions. Note that the
+// word "inline" itself may now be redefined by the above #define.
+#define BX_CPP_INLINE inline
+
+#ifdef __GNUC__
+
+// Some helpful compiler hints for compilers that allow them; GCC for now.
+//
+// BX_CPP_AlignN(n):
+// Align a construct on an n-byte boundary.
+//
+// BX_CPP_AttrPrintf(formatArg, firstArg):
+// This function takes printf-like arguments, so the compiler can check
+// the consistency of the format string and the matching arguments.
+// 'formatArg' is the parameter number (starting from 1) of the format
+// string argument. 'firstArg' is the parameter number of the 1st argument
+// to check against the string argument. NOTE: For non-static member
+// functions, the this-ptr is argument number 1 but is invisible on
+// the function prototype declaration - but you still have to count it.
+//
+// BX_CPP_AttrNoReturn():
+// This function never returns. The compiler can optimize-out following
+// code accordingly.
+
+#define BX_CPP_AlignN(n) __attribute__ ((aligned (n)))
+#define BX_CPP_AttrPrintf(formatArg, firstArg) \
+ __attribute__ ((format (printf, formatArg, firstArg)))
+#define BX_CPP_AttrNoReturn() __attribute__ ((noreturn))
+
+#else
+
+#define BX_CPP_AlignN(n) /* Not supported. */
+#define BX_CPP_AttrPrintf(formatArg, firstArg) /* Not supported. */
+#define BX_CPP_AttrNoReturn() /* Not supported. */
+
+#endif
+
+#define BX_DEBUGGER 0
+#define BX_DISASM 0
+
+#define BX_PROVIDE_CPU_MEMORY 1
+#define BX_PROVIDE_DEVICE_MODELS 1
+
+#define BX_SUPPORT_VBE 0
+
+#define BX_PROVIDE_MAIN 1
+
+#define BX_INSTRUMENTATION 0
+
+#define BX_USE_LOADER 0
+
+// for debugger, CPU simulator handle ID
+// 0 is the default, for using only one CPU simulator
+// 1 is for the 2nd CPU simulator
+#define BX_SIM_ID 0
+#define BX_NUM_SIMULATORS 1
+
+// limited i440FX PCI support
+#define BX_PCI_SUPPORT 1
+
+// Experimental VGA on PCI
+#define BX_PCI_VGA_SUPPORT 1
+
+// limited USB on PCI
+#define BX_PCI_USB_SUPPORT 1
+
+#if (BX_PCI_USB_SUPPORT && !BX_PCI_SUPPORT)
+#error To enable USB, you must also enable PCI
+#endif
+
+// Promise VLBIDE DC2300 Support
+#define BX_PDC20230C_VLBIDE_SUPPORT 0
+
+#define BX_SUPPORT_FPU 1
+#define BX_SUPPORT_MMX 1
+#define BX_SUPPORT_3DNOW 0
+#define BX_SUPPORT_SSE 0
+#define BX_SUPPORT_DAZ 0
+#define BX_SUPPORT_PNI 0
+#define BX_SUPPORT_SEP 0
+#define BX_SUPPORT_4MEG_PAGES 0
+
+#define BX_SupportGuest2HostTLB 0
+#define BX_SupportRepeatSpeedups 0
+#define BX_SupportGlobalPages 0
+#define BX_SupportPAE 0
+#define BX_SupportICache 0
+#define BX_SupportHostAsms 1
+
+
+// if 1, don't do gpf on MSRs that we don't implement
+#define BX_IGNORE_BAD_MSR 0
+
+// ========================================================
+// These are some very temporary hacks I made to the 64-bit
+// support to help Peter with debugging etc. They will be removed
+// soon and there is no configure option for them (on purpose).
+// By default, they are not compiled in.
+
+// I set this to 1 to bail in instructions which may not be honoring
+// the 64-bit widths of RIP/RSP. If I trip a panic, then I clean
+// the function up and remove the panic. You don't have to use
+// these.
+#if 0
+#define BailBigRSP(s) \
+ if ( (RIP > 0xffffffff) || \
+ (RSP > 0xffffffff) ) \
+ BX_PANIC((s ": bailing due to big RSP value, mode==%u", \
+ BX_CPU_THIS_PTR cpu_mode))
+#else
+#define BailBigRSP(s)
+#endif
+// ========================================================
+
+
+#if (BX_SUPPORT_MMX && BX_CPU_LEVEL < 5)
+#error With CPU level < 5, you must disable MMX support.
+#endif
+
+#if (!BX_SUPPORT_FPU && BX_CPU_LEVEL >= 5)
+#error With CPU level >= 5, you must enable FPU support.
+#endif
+
+#if (BX_SUPPORT_MMX && !BX_SUPPORT_FPU)
+#error "MMX cannot be compiled without FPU support"
+#endif
+
+#if (BX_SUPPORT_3DNOW && !BX_SUPPORT_MMX)
+#error "3DNow! cannot be compiled without MMX support"
+#endif
+
+#if (BX_SUPPORT_SSE && !BX_SUPPORT_MMX)
+#error "SSE cannot be compiled without FPU+MMX support"
+#endif
+
+#if (BX_CPU_LEVEL<6 && BX_SUPPORT_SSE)
+#error SSE is only supported with CPU_LEVEL >= 6
+#endif
+
+#if (BX_SUPPORT_PNI && BX_SUPPORT_SSE <= 1)
+#error "PNI cannot be compiled without SSE/SSE2 support"
+#endif
+
+#if (BX_CPU_LEVEL<6 && BX_SUPPORT_SEP)
+#error SYSENTER/SYSEXIT only supported with CPU_LEVEL >= 6
+#endif
+
+#if BX_SUPPORT_X86_64
+// Sanity checks to ensure that you cannot accidently use conflicting options.
+
+#if BX_CPU_LEVEL < 5
+#error X86-64 requires cpu level 6 or greater
+#endif
+#if (BX_SUPPORT_SSE<2)
+#error X86-64 requires SSE2
+#endif
+#if !BX_SupportPAE
+#error X86-64 requires Physical Address Extensions (PAE)
+#endif
+#if !BX_SupportGlobalPages
+#error X86-64 requires Page Global Extension (PGE)
+#endif
+#if !BX_SUPPORT_4MEG_PAGES
+#error X86-64 requires Page Size Extension (PSE)
+#endif
+
+#if BX_SUPPORT_SEP
+#error SYSENTER/SYSEXIT not implemented for X86-64
+#endif
+
+#endif
+
+#define BX_HAVE_GETENV 1
+#define BX_HAVE_SETENV 1
+#define BX_HAVE_SELECT 1
+#define BX_HAVE_SNPRINTF 1
+#define BX_HAVE_STRTOULL 1
+#define BX_HAVE_STRTOUQ 1
+#define BX_HAVE_STRDUP 1
+#define BX_HAVE_STRREV 0
+#define BX_HAVE_STRUCT_TIMEVAL 1
+
+// used in term gui
+#define BX_HAVE_COLOR_SET 0
+#define BX_HAVE_MVHLINE 0
+#define BX_HAVE_MVVLINE 0
+
+
+// set if your compiler does not permit an empty struct
+#define BX_NO_EMPTY_STRUCTS 0
+
+// set if your compiler does not understand __attribute__ after a struct
+#define BX_NO_ATTRIBUTES 0
+#if BX_NO_ATTRIBUTES
+#define GCC_ATTRIBUTE(x) /* attribute not supported */
+#else
+#define GCC_ATTRIBUTE __attribute__
+#endif
+
+// set to use fast function calls
+#define BX_FAST_FUNC_CALL 0
+
+// On gcc2.95+ x86 only
+#if BX_FAST_FUNC_CALL && defined(__i386__) && defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95))
+# define BX_CPP_AttrRegparmN(X) __attribute__((regparm(X)))
+#else
+# define BX_CPP_AttrRegparmN(X) /* Not defined */
+#endif
+
+// set if your compiler does not allow label at the end of a {} block
+#define BX_NO_BLANK_LABELS 0
+
+// set if you do have <hash_map>, used in bx_debug/dbg_main.c
+#define BX_HAVE_HASH_MAP 0
+
+// set if you do have <hash_map.h>, used in bx_debug/dbg_main.c
+#define BX_HAVE_HASH_MAP_H 1
+
+// set if you do have <set>, used in bx_debug/dbg_main.c
+#define BX_HAVE_SET 1
+
+// set if you do have <set.h>, used in bx_debug/dbg_main.c
+#define BX_HAVE_SET_H 1
+
+// Support x86 hardware debugger registers and facilites.
+// These are the debug facilites offered by the x86 architecture,
+// not the optional built-in debugger.
+#define BX_X86_DEBUGGER 0
+
+#define BX_SUPPORT_CDROM 1
+
+#if BX_SUPPORT_CDROM
+ // This is the C++ class name to use if we are supporting
+ // low-level CDROM.
+# define LOWLEVEL_CDROM cdrom_interface
+#endif
+
+// NE2K network emulation
+#define BX_NE2K_SUPPORT 1
+#define BX_ETH_NULL_LOGGING 1
+#define BX_ETH_FBSD_LOGGING 1
+
+// determine which NE2K packet mover modules will be enabled
+// (this was moved from iodev/eth.h)
+#define ETH_NULL 1
+#ifdef BX_USE_ETH_ARPBACK
+# define ETH_ARPBACK 1
+#endif
+#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#define ETH_FBSD 1
+#endif
+#if defined(linux)
+#define ETH_LINUX 1
+#endif
+#if defined(WIN32)
+#define ETH_WIN32 1
+#endif
+
+// this enables Ethertap packet mover; determined by configure script
+#define HAVE_ETHERTAP 0
+
+// this enables TUN/TAP packet mover; determined by configure script
+#define HAVE_TUNTAP 1
+
+
+// I/O Interface to debug
+#define BX_IODEBUG_SUPPORT 0
+
+// External Debugger
+#define BX_EXTERNAL_DEBUGGER 0
+#define BX_OVERRIDE_ASK 0
+
+#ifdef WIN32
+#define BX_FLOPPY0_NAME "Floppy Disk A:"
+#define BX_FLOPPY1_NAME "Floppy Disk B:"
+#else
+#define BX_FLOPPY0_NAME "Floppy Disk 0"
+#define BX_FLOPPY1_NAME "Floppy Disk 1"
+#endif
+
+// This is handy for certain performance testing purposes, but otherwise
+// totally useless. If you define BX_SCHEDULED_DIE_TIME then it enables code
+// in bx_pit_c::periodic that will cause Bochs to exit() after a certain number
+// of instructions.
+//#define BX_SCHEDULED_DIE_TIME 1162230000 // end of redhat6.0 boot
+
+
+#endif // _BX_CONFIG_H
diff --git a/tools/ioemu/include/cpu/cpu.h b/tools/ioemu/include/cpu/cpu.h
new file mode 100644
index 0000000000..0627ee385e
--- /dev/null
+++ b/tools/ioemu/include/cpu/cpu.h
@@ -0,0 +1,116 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: cpu.h,v 1.155 2003/12/30 22:12:45 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#ifndef BX_CPU_H
+# define BX_CPU_H 1
+
+#include <setjmp.h>
+#ifdef BX_USE_VMX
+extern "C" {
+#include <io/ioreq.h>
+}
+#endif
+
+
+#if BX_SUPPORT_APIC
+#define BX_CPU_INTR (BX_CPU_THIS_PTR INTR || BX_CPU_THIS_PTR local_apic.INTR)
+#else
+#define BX_CPU_INTR BX_CPU_THIS_PTR INTR
+#endif
+
+class BX_CPU_C;
+class BX_MEM_C;
+
+#if BX_USE_CPU_SMF == 0
+// normal member functions. This can ONLY be used within BX_CPU_C classes.
+// Anyone on the outside should use the BX_CPU macro (defined in bochs.h)
+// instead.
+# define BX_CPU_THIS_PTR this->
+# define BX_CPU_THIS this
+# define BX_SMF
+# define BX_CPU_C_PREFIX BX_CPU_C::
+// with normal member functions, calling a member fn pointer looks like
+// object->*(fnptr)(arg, ...);
+// Since this is different from when SMF=1, encapsulate it in a macro.
+# define BX_CPU_CALL_METHOD(func, args) \
+ (this->*((BxExecutePtr_t) (func))) args
+# define BX_CPU_CALL_METHODR(func, args) \
+ (this->*((BxExecutePtr_tR) (func))) args
+#else
+// static member functions. With SMF, there is only one CPU by definition.
+# define BX_CPU_THIS_PTR BX_CPU(0)->
+# define BX_CPU_THIS BX_CPU(0)
+# define BX_SMF static
+# define BX_CPU_C_PREFIX
+# define BX_CPU_CALL_METHOD(func, args) \
+ ((BxExecutePtr_t) (func)) args
+# define BX_CPU_CALL_METHODR(func, args) \
+ ((BxExecutePtr_tR) (func)) args
+#endif
+
+#if BX_SMP_PROCESSORS==1
+// single processor simulation, so there's one of everything
+BOCHSAPI extern BX_CPU_C bx_cpu;
+#else
+// multiprocessor simulation, we need an array of cpus and memories
+BOCHSAPI extern BX_CPU_C *bx_cpu_array[BX_SMP_PROCESSORS];
+#endif
+
+class BOCHSAPI BX_CPU_C : public logfunctions {
+
+public: // for now...
+
+ volatile bx_bool async_event;
+ volatile bx_bool INTR;
+ volatile bx_bool kill_bochs_request;
+
+ // constructors & destructors...
+ BX_CPU_C();
+ ~BX_CPU_C(void);
+ void init (BX_MEM_C *addrspace);
+ void interrupt(Bit8u vector);
+
+ BX_SMF void pagingA20Changed(void);
+ BX_SMF void reset(unsigned source);
+ BX_SMF void set_INTR(bx_bool value);
+ BX_SMF void atexit(void);
+
+ // now for some ancillary functions...
+ void cpu_loop(Bit32s max_instr_count);
+
+#ifdef BX_USE_VMX
+ ioreq_t* __get_ioreq(void);
+ ioreq_t* get_ioreq(void);
+ void dispatch_ioreq(ioreq_t *req);
+ void handle_ioreq();
+ void timer_handler();
+
+ int send_event;
+#endif
+};
+
+#endif // #ifndef BX_CPU_H
diff --git a/tools/ioemu/include/extplugin.h b/tools/ioemu/include/extplugin.h
new file mode 100644
index 0000000000..f3e43f79d4
--- /dev/null
+++ b/tools/ioemu/include/extplugin.h
@@ -0,0 +1,51 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: extplugin.h,v 1.4 2002/12/12 15:28:37 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// extplugin.h
+//
+// This header file defines the types necessary to make a Bochs plugin,
+// but without mentioning all the details of Bochs internals (bochs.h).
+// It is included by the configuration interfaces and possibly other
+// things which are intentionally isolated from other parts of the program.
+//
+// The plugin_t struct comes from the plugin.h file from plex86.
+// Plex86 is Copyright (C) 1999-2000 The plex86 developers team
+//
+/////////////////////////////////////////////////////////////////////////
+
+#ifndef __EXTPLUGIN_H
+#define __EXTPLUGIN_H
+
+#if BX_PLUGINS
+#include "ltdl.h"
+#endif
+
+enum plugintype_t {
+ PLUGTYPE_NULL=100,
+ PLUGTYPE_CORE,
+ PLUGTYPE_OPTIONAL,
+ PLUGTYPE_USER
+};
+
+#define MAX_ARGC 10
+
+typedef struct _plugin_t
+{
+ plugintype_t type;
+ int initialized;
+#if BX_PLUGINS
+ lt_dlhandle handle;
+#endif
+ int argc;
+ char *name, *args, *argv[MAX_ARGC];
+ int (*plugin_init)(struct _plugin_t *plugin, plugintype_t type, int argc, char *argv[]);
+ void (*plugin_fini)(void);
+
+ struct _plugin_t *next;
+} plugin_t;
+
+
+
+#endif /* __EXTPLUGIN_H */
+
diff --git a/tools/ioemu/include/instrument.h b/tools/ioemu/include/instrument.h
new file mode 100644
index 0000000000..8d753ff7d7
--- /dev/null
+++ b/tools/ioemu/include/instrument.h
@@ -0,0 +1,256 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: instrument.h,v 1.14 2003/10/09 19:05:13 sshwarts Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// possible types passed to BX_INSTR_TLB_CNTRL()
+#define BX_INSTR_MOV_CR3 10
+#define BX_INSTR_INVLPG 11
+#define BX_INSTR_TASKSWITCH 12
+
+// possible types passed to BX_INSTR_CACHE_CNTRL()
+#define BX_INSTR_INVD 20
+#define BX_INSTR_WBINVD 21
+
+#define BX_INSTR_IS_CALL 10
+#define BX_INSTR_IS_RET 11
+#define BX_INSTR_IS_IRET 12
+#define BX_INSTR_IS_JMP 13
+#define BX_INSTR_IS_INT 14
+
+#define BX_INSTR_PREFETCH_NTA 00
+#define BX_INSTR_PREFETCH_T0 01
+#define BX_INSTR_PREFETCH_T1 02
+#define BX_INSTR_PREFETCH_T2 03
+
+
+
+
+
+#if BX_INSTRUMENTATION
+
+class bxInstruction_c;
+
+// called from the CPU core
+
+void bx_instr_init(unsigned cpu);
+void bx_instr_shutdown(unsigned cpu);
+void bx_instr_reset(unsigned cpu);
+void bx_instr_new_instruction(unsigned cpu);
+
+void bx_instr_debug_promt();
+void bx_instr_start();
+void bx_instr_stop();
+void bx_instr_print();
+
+void bx_instr_cnear_branch_taken(unsigned cpu, bx_address new_eip);
+void bx_instr_cnear_branch_not_taken(unsigned cpu);
+void bx_instr_ucnear_branch(unsigned cpu, unsigned what, bx_address new_eip);
+void bx_instr_far_branch(unsigned cpu, unsigned what, Bit16u new_cs, bx_address new_eip);
+
+void bx_instr_opcode(unsigned cpu, Bit8u *opcode, unsigned len, bx_bool is32);
+void bx_instr_fetch_decode_completed(unsigned cpu, const bxInstruction_c *i);
+
+void bx_instr_prefix_as(unsigned cpu);
+void bx_instr_prefix_os(unsigned cpu);
+void bx_instr_prefix_rep(unsigned cpu);
+void bx_instr_prefix_repne(unsigned cpu);
+void bx_instr_prefix_lock(unsigned cpu);
+void bx_instr_prefix_cs(unsigned cpu);
+void bx_instr_prefix_ss(unsigned cpu);
+void bx_instr_prefix_ds(unsigned cpu);
+void bx_instr_prefix_es(unsigned cpu);
+void bx_instr_prefix_fs(unsigned cpu);
+void bx_instr_prefix_gs(unsigned cpu);
+void bx_instr_prefix_extend8b(unsigned cpu);
+
+void bx_instr_interrupt(unsigned cpu, unsigned vector);
+void bx_instr_exception(unsigned cpu, unsigned vector);
+void bx_instr_hwinterrupt(unsigned cpu, unsigned vector, Bit16u cs, bx_address eip);
+
+void bx_instr_tlb_cntrl(unsigned cpu, unsigned what, Bit32u newval);
+void bx_instr_cache_cntrl(unsigned cpu, unsigned what);
+void bx_instr_prefetch_hint(unsigned cpu, unsigned what, unsigned seg, bx_address offset);
+
+void bx_instr_before_execution(unsigned cpu);
+void bx_instr_after_execution(unsigned cpu);
+void bx_instr_repeat_iteration(unsigned cpu);
+
+void bx_instr_inp(Bit16u addr, unsigned len);
+void bx_instr_outp(Bit16u addr, unsigned len);
+void bx_instr_inp2(Bit16u addr, unsigned len, unsigned val);
+void bx_instr_outp2(Bit16u addr, unsigned len, unsigned val);
+
+void bx_instr_mem_code(unsigned cpu, bx_address linear, unsigned size);
+void bx_instr_mem_data(unsigned cpu, bx_address linear, unsigned size, unsigned rw);
+
+void bx_instr_lin_read(unsigned cpu, bx_address lin, bx_address phy, unsigned len);
+void bx_instr_lin_write(unsigned cpu, bx_address lin, bx_address phy, unsigned len);
+
+void bx_instr_phy_write(unsigned cpu, bx_address addr, unsigned len);
+void bx_instr_phy_read(unsigned cpu, bx_address addr, unsigned len);
+
+/* simulation init, shutdown, reset */
+# define BX_INSTR_INIT(cpu_id) bx_instr_init(cpu_id)
+# define BX_INSTR_SHUTDOWN(cpu_id) bx_instr_shutdown(cpu_id)
+# define BX_INSTR_RESET(cpu_id) bx_instr_reset(cpu_id)
+# define BX_INSTR_NEW_INSTRUCTION(cpu_id) bx_instr_new_instruction(cpu_id)
+
+/* called from command line debugger */
+# define BX_INSTR_DEBUG_PROMPT() bx_instr_debug_promt()
+# define BX_INSTR_START() bx_instr_start()
+# define BX_INSTR_STOP() bx_instr_stop()
+# define BX_INSTR_PRINT() bx_instr_print()
+
+/* branch resoultion */
+# define BX_INSTR_CNEAR_BRANCH_TAKEN(cpu_id, new_eip) bx_instr_cnear_branch_taken(cpu_id, new_eip)
+# define BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(cpu_id) bx_instr_cnear_branch_not_taken(cpu_id)
+# define BX_INSTR_UCNEAR_BRANCH(cpu_id, what, new_eip) bx_instr_ucnear_branch(cpu_id, what, new_eip)
+# define BX_INSTR_FAR_BRANCH(cpu_id, what, new_cs, new_eip) bx_instr_far_branch(cpu_id, what, new_cs, new_eip)
+
+/* decoding completed */
+# define BX_INSTR_OPCODE(cpu_id, opcode, len, is32) \
+ bx_instr_opcode(cpu_id, opcode, len, is32)
+# define BX_INSTR_FETCH_DECODE_COMPLETED(cpu_id, i) \
+ bx_instr_fetch_decode_completed(cpu_id, i)
+
+/* prefix decoded */
+# define BX_INSTR_PREFIX_AS(cpu_id) bx_instr_prefix_as(cpu_id)
+# define BX_INSTR_PREFIX_OS(cpu_id) bx_instr_prefix_os(cpu_id)
+# define BX_INSTR_PREFIX_REP(cpu_id) bx_instr_prefix_rep(cpu_id)
+# define BX_INSTR_PREFIX_REPNE(cpu_id) bx_instr_prefix_repne(cpu_id)
+# define BX_INSTR_PREFIX_LOCK(cpu_id) bx_instr_prefix_lock(cpu_id)
+# define BX_INSTR_PREFIX_CS(cpu_id) bx_instr_prefix_cs(cpu_id)
+# define BX_INSTR_PREFIX_SS(cpu_id) bx_instr_prefix_ss(cpu_id)
+# define BX_INSTR_PREFIX_DS(cpu_id) bx_instr_prefix_ds(cpu_id)
+# define BX_INSTR_PREFIX_ES(cpu_id) bx_instr_prefix_es(cpu_id)
+# define BX_INSTR_PREFIX_FS(cpu_id) bx_instr_prefix_fs(cpu_id)
+# define BX_INSTR_PREFIX_GS(cpu_id) bx_instr_prefix_gs(cpu_id)
+# define BX_INSTR_PREFIX_EXTEND8B(cpu_id) bx_instr_prefix_extend8b(cpu_id)
+
+/* exceptional case and interrupt */
+# define BX_INSTR_EXCEPTION(cpu_id, vector) bx_instr_exception(cpu_id, vector)
+# define BX_INSTR_INTERRUPT(cpu_id, vector) bx_instr_interrupt(cpu_id, vector)
+# define BX_INSTR_HWINTERRUPT(cpu_id, vector, cs, eip) bx_instr_hwinterrupt(cpu_id, vector, cs, eip)
+
+/* TLB/CACHE control instruction executed */
+# define BX_INSTR_CACHE_CNTRL(cpu_id, what) bx_instr_cache_cntrl(cpu_id, what)
+# define BX_INSTR_TLB_CNTRL(cpu_id, what, newval) bx_instr_tlb_cntrl(cpu_id, what, newval)
+# define BX_INSTR_PREFETCH_HINT(cpu_id, what, seg, offset) \
+ bx_instr_prefetch_hint(cpu_id, what, seg, offset)
+
+/* execution */
+# define BX_INSTR_BEFORE_EXECUTION(cpu_id) bx_instr_before_execution(cpu_id)
+# define BX_INSTR_AFTER_EXECUTION(cpu_id) bx_instr_after_execution(cpu_id)
+# define BX_INSTR_REPEAT_ITERATION(cpu_id) bx_instr_repeat_iteration(cpu_id)
+
+/* memory access */
+# define BX_INSTR_LIN_READ(cpu_id, lin, phy, len) bx_instr_lin_read(cpu_id, lin, phy, len)
+# define BX_INSTR_LIN_WRITE(cpu_id, lin, phy, len) bx_instr_lin_write(cpu_id, lin, phy, len)
+
+# define BX_INSTR_MEM_CODE(cpu_id, linear, size) bx_instr_mem_code(cpu_id, linear, size)
+# define BX_INSTR_MEM_DATA(cpu_id, linear, size, rw) bx_instr_mem_data(cpu_id, linear, size, rw)
+
+/* called from memory object */
+# define BX_INSTR_PHY_WRITE(cpu_id, addr, len) bx_instr_phy_write(cpu_id, addr, len)
+# define BX_INSTR_PHY_READ(cpu_id, addr, len) bx_instr_phy_read(cpu_id, addr, len)
+
+/* feedback from device units */
+# define BX_INSTR_INP(addr, len) bx_instr_inp(addr, len)
+# define BX_INSTR_INP2(addr, len, val) bx_instr_inp2(addr, len, val)
+# define BX_INSTR_OUTP(addr, len) bx_instr_outp(addr, len)
+# define BX_INSTR_OUTP2(addr, len, val) bx_instr_outp2(addr, len, val)
+
+#else
+
+/* simulation init, shutdown, reset */
+# define BX_INSTR_INIT(cpu_id)
+# define BX_INSTR_SHUTDOWN(cpu_id)
+# define BX_INSTR_RESET(cpu_id)
+# define BX_INSTR_NEW_INSTRUCTION(cpu_id)
+
+/* called from command line debugger */
+# define BX_INSTR_DEBUG_PROMPT()
+# define BX_INSTR_START()
+# define BX_INSTR_STOP()
+# define BX_INSTR_PRINT()
+
+/* branch resoultion */
+# define BX_INSTR_CNEAR_BRANCH_TAKEN(cpu_id, new_eip)
+# define BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(cpu_id)
+# define BX_INSTR_UCNEAR_BRANCH(cpu_id, what, new_eip)
+# define BX_INSTR_FAR_BRANCH(cpu_id, what, new_cs, new_eip)
+
+/* decoding completed */
+# define BX_INSTR_OPCODE(cpu_id, opcode, len, is32)
+# define BX_INSTR_FETCH_DECODE_COMPLETED(cpu_id, i)
+
+/* prefix decoded */
+# define BX_INSTR_PREFIX_AS(cpu_id)
+# define BX_INSTR_PREFIX_OS(cpu_id)
+# define BX_INSTR_PREFIX_REP(cpu_id)
+# define BX_INSTR_PREFIX_REPNE(cpu_id)
+# define BX_INSTR_PREFIX_LOCK(cpu_id)
+# define BX_INSTR_PREFIX_CS(cpu_id)
+# define BX_INSTR_PREFIX_SS(cpu_id)
+# define BX_INSTR_PREFIX_DS(cpu_id)
+# define BX_INSTR_PREFIX_ES(cpu_id)
+# define BX_INSTR_PREFIX_FS(cpu_id)
+# define BX_INSTR_PREFIX_GS(cpu_id)
+# define BX_INSTR_PREFIX_EXTEND8B(cpu_id)
+
+/* exceptional case and interrupt */
+# define BX_INSTR_EXCEPTION(cpu_id, vector)
+# define BX_INSTR_INTERRUPT(cpu_id, vector)
+# define BX_INSTR_HWINTERRUPT(cpu_id, vector, cs, eip)
+
+/* TLB/CACHE control instruction executed */
+# define BX_INSTR_CACHE_CNTRL(cpu_id, what)
+# define BX_INSTR_TLB_CNTRL(cpu_id, what, newval)
+# define BX_INSTR_PREFETCH_HINT(cpu_id, what, seg, offset)
+
+/* execution */
+# define BX_INSTR_BEFORE_EXECUTION(cpu_id)
+# define BX_INSTR_AFTER_EXECUTION(cpu_id)
+# define BX_INSTR_REPEAT_ITERATION(cpu_id)
+
+/* memory access */
+# define BX_INSTR_LIN_READ(cpu_id, lin, phy, len)
+# define BX_INSTR_LIN_WRITE(cpu_id, lin, phy, len)
+
+# define BX_INSTR_MEM_CODE(cpu_id, linear, size)
+# define BX_INSTR_MEM_DATA(cpu_id, linear, size, rw)
+
+/* called from memory object */
+# define BX_INSTR_PHY_WRITE(cpu_id, addr, len)
+# define BX_INSTR_PHY_READ(cpu_id, addr, len)
+
+/* feedback from device units */
+# define BX_INSTR_INP(addr, len)
+# define BX_INSTR_INP2(addr, len, val)
+# define BX_INSTR_OUTP(addr, len)
+# define BX_INSTR_OUTP2(addr, len, val)
+
+#endif
diff --git a/tools/ioemu/include/ltdl.h b/tools/ioemu/include/ltdl.h
new file mode 100644
index 0000000000..3a02b311c2
--- /dev/null
+++ b/tools/ioemu/include/ltdl.h
@@ -0,0 +1,398 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: ltdl.h,v 1.2 2002/10/24 21:04:37 bdenney Exp $
+//
+// NOTE: The ltdl library comes from the Libtool package. Bochs uses
+// ltdl and libtool to build and load plugins. The libtool
+// documentation describes how to copy ltdl.c and ltdl.h into your
+// distribution, so it is clearly legal to do so.
+/////////////////////////////////////////////////////////////////////////
+
+/* ltdl.h -- generic dlopen functions
+ Copyright (C) 1998-2000 Free Software Foundation, Inc.
+ Originally by Thomas Tanner <tanner@ffii.org>
+ This file is part of GNU Libtool.
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+As a special exception to the GNU Lesser General Public License,
+if you distribute this file as part of a program or library that
+is built using GNU libtool, you may include it under the same
+distribution terms that you use for the rest of that program.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free
+Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA
+*/
+
+/* Only include this header file once. */
+#ifndef LTDL_H
+#define LTDL_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <sys/types.h> /* for size_t declaration */
+#ifdef __cplusplus
+};
+#endif
+
+
+/* --- MACROS FOR PORTABILITY --- */
+
+
+/* Saves on those hard to debug '\0' typos.... */
+#define LT_EOS_CHAR '\0'
+
+/* LTDL_BEGIN_C_DECLS should be used at the beginning of your declarations,
+ so that C++ compilers don't mangle their names. Use LTDL_END_C_DECLS at
+ the end of C declarations. */
+#ifdef __cplusplus
+# define LT_BEGIN_C_DECLS extern "C" {
+# define LT_END_C_DECLS }
+#else
+# define LT_BEGIN_C_DECLS /* empty */
+# define LT_END_C_DECLS /* empty */
+#endif
+
+LT_BEGIN_C_DECLS
+
+
+/* LT_PARAMS is a macro used to wrap function prototypes, so that compilers
+ that don't understand ANSI C prototypes still work, and ANSI C
+ compilers can issue warnings about type mismatches. */
+#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) || defined(__cplusplus)
+# define LT_PARAMS(protos) protos
+# define lt_ptr void*
+#else
+# define LT_PARAMS(protos) ()
+# define lt_ptr char*
+#endif
+
+/* LT_STMT_START/END are used to create macros which expand to a
+ a single compound statement in a portable way. */
+#if defined (__GNUC__) && !defined (__STRICT_ANSI__) && !defined (__cplusplus)
+# define LT_STMT_START (void)(
+# define LT_STMT_END )
+#else
+# if (defined (sun) || defined (__sun__))
+# define LT_STMT_START if (1)
+# define LT_STMT_END else (void)0
+# else
+# define LT_STMT_START do
+# define LT_STMT_END while (0)
+# endif
+#endif
+
+/* LT_CONC creates a new concatenated symbol for the compiler
+ in a portable way. */
+#if defined(__STDC__) || defined(__cplusplus)
+# define LT_CONC(s,t) s##t
+#else
+# define LT_CONC(s,t) s/**/t
+#endif
+
+/* LT_STRLEN can be used safely on NULL pointers. */
+#define LT_STRLEN(s) (((s) && (s)[0]) ? strlen (s) : 0)
+
+
+
+/* --- WINDOWS SUPPORT --- */
+
+
+/* Canonicalise Windows and Cygwin recognition macros. */
+#ifdef __CYGWIN32__
+# ifndef __CYGWIN__
+# define __CYGWIN__ __CYGWIN32__
+# endif
+#endif
+#if defined(_WIN32) || defined(WIN32)
+# ifndef __WINDOWS__
+# ifdef _WIN32
+# define __WINDOWS__ _WIN32
+# else
+# ifdef WIN32
+# define __WINDOWS__ WIN32
+# endif
+# endif
+# endif
+#endif
+
+#ifdef __WINDOWS__
+# ifndef __CYGWIN__
+/* LT_DIRSEP_CHAR is accepted *in addition* to '/' as a directory
+ separator when it is set. */
+# define LT_DIRSEP_CHAR '\\'
+# define LT_PATHSEP_CHAR ';'
+# endif
+#endif
+#ifndef LT_PATHSEP_CHAR
+# define LT_PATHSEP_CHAR ':'
+#endif
+
+/* DLL building support on win32 hosts; mostly to workaround their
+ ridiculous implementation of data symbol exporting. */
+#ifndef LT_SCOPE
+# ifdef __WINDOWS__
+# ifdef DLL_EXPORT /* defined by libtool (if required) */
+# define LT_SCOPE __declspec(dllexport)
+# endif
+# ifdef LIBLTDL_DLL_IMPORT /* define if linking with this dll */
+# define LT_SCOPE extern __declspec(dllimport)
+# endif
+# endif
+# ifndef LT_SCOPE /* static linking or !__WINDOWS__ */
+# define LT_SCOPE extern
+# endif
+#endif
+
+
+
+
+/* --- DYNAMIC MODULE LOADING API --- */
+
+
+typedef struct lt_dlhandle_struct *lt_dlhandle; /* A loaded module. */
+
+/* Initialisation and finalisation functions for libltdl. */
+extern int lt_dlinit LT_PARAMS((void));
+extern int lt_dlexit LT_PARAMS((void));
+
+/* Module search path manipulation. */
+extern int lt_dladdsearchdir LT_PARAMS((const char *search_dir));
+extern int lt_dlinsertsearchdir LT_PARAMS((const char *before,
+ const char *search_dir));
+extern int lt_dlsetsearchpath LT_PARAMS((const char *search_path));
+extern const char *lt_dlgetsearchpath LT_PARAMS((void));
+extern int lt_dlforeachfile LT_PARAMS((
+ const char *search_path,
+ int (*func) (const char *filename, lt_ptr data),
+ lt_ptr data));
+
+/* Portable libltdl versions of the system dlopen() API. */
+extern lt_dlhandle lt_dlopen LT_PARAMS((const char *filename));
+extern lt_dlhandle lt_dlopenext LT_PARAMS((const char *filename));
+extern lt_ptr lt_dlsym LT_PARAMS((lt_dlhandle handle,
+ const char *name));
+extern const char *lt_dlerror LT_PARAMS((void));
+extern int lt_dlclose LT_PARAMS((lt_dlhandle handle));
+
+/* Module residency management. */
+extern int lt_dlmakeresident LT_PARAMS((lt_dlhandle handle));
+extern int lt_dlisresident LT_PARAMS((lt_dlhandle handle));
+
+
+
+
+/* --- MUTEX LOCKING --- */
+
+
+typedef void lt_dlmutex_lock LT_PARAMS((void));
+typedef void lt_dlmutex_unlock LT_PARAMS((void));
+typedef void lt_dlmutex_seterror LT_PARAMS((const char *errmsg));
+typedef const char *lt_dlmutex_geterror LT_PARAMS((void));
+
+extern int lt_dlmutex_register LT_PARAMS((lt_dlmutex_lock *lock,
+ lt_dlmutex_unlock *unlock,
+ lt_dlmutex_seterror *seterror,
+ lt_dlmutex_geterror *geterror));
+
+
+
+
+/* --- MEMORY HANDLING --- */
+
+
+/* By default, the realloc function pointer is set to our internal
+ realloc implementation which iself uses lt_dlmalloc and lt_dlfree.
+ libltdl relies on a featureful realloc, but if you are sure yours
+ has the right semantics then you can assign it directly. Generally,
+ it is safe to assign just a malloc() and a free() function. */
+LT_SCOPE lt_ptr (*lt_dlmalloc) LT_PARAMS((size_t size));
+LT_SCOPE lt_ptr (*lt_dlrealloc) LT_PARAMS((lt_ptr ptr, size_t size));
+LT_SCOPE void (*lt_dlfree) LT_PARAMS((lt_ptr ptr));
+
+
+
+
+/* --- PRELOADED MODULE SUPPORT --- */
+
+
+/* A preopened symbol. Arrays of this type comprise the exported
+ symbols for a dlpreopened module. */
+typedef struct {
+ const char *name;
+ lt_ptr address;
+} lt_dlsymlist;
+
+extern int lt_dlpreload LT_PARAMS((const lt_dlsymlist *preloaded));
+extern int lt_dlpreload_default
+ LT_PARAMS((const lt_dlsymlist *preloaded));
+
+#define LTDL_SET_PRELOADED_SYMBOLS() LT_STMT_START{ \
+ extern const lt_dlsymlist lt_preloaded_symbols[]; \
+ lt_dlpreload_default(lt_preloaded_symbols); \
+ }LT_STMT_END
+
+
+
+
+/* --- MODULE INFORMATION --- */
+
+
+/* Read only information pertaining to a loaded module. */
+typedef struct {
+ char *filename; /* file name */
+ char *name; /* module name */
+ int ref_count; /* number of times lt_dlopened minus
+ number of times lt_dlclosed. */
+} lt_dlinfo;
+
+extern const lt_dlinfo *lt_dlgetinfo LT_PARAMS((lt_dlhandle handle));
+extern lt_dlhandle lt_dlhandle_next LT_PARAMS((lt_dlhandle place));
+extern int lt_dlforeach LT_PARAMS((
+ int (*func) (lt_dlhandle handle, lt_ptr data),
+ lt_ptr data));
+
+/* Associating user data with loaded modules. */
+typedef unsigned lt_dlcaller_id;
+
+extern lt_dlcaller_id lt_dlcaller_register LT_PARAMS((void));
+extern lt_ptr lt_dlcaller_set_data LT_PARAMS((lt_dlcaller_id key,
+ lt_dlhandle handle,
+ lt_ptr data));
+extern lt_ptr lt_dlcaller_get_data LT_PARAMS((lt_dlcaller_id key,
+ lt_dlhandle handle));
+
+
+
+/* --- USER MODULE LOADER API --- */
+
+
+typedef struct lt_dlloader lt_dlloader;
+typedef lt_ptr lt_user_data;
+typedef lt_ptr lt_module;
+
+/* Function pointer types for creating user defined module loaders. */
+typedef lt_module lt_module_open LT_PARAMS((lt_user_data loader_data,
+ const char *filename));
+typedef int lt_module_close LT_PARAMS((lt_user_data loader_data,
+ lt_module handle));
+typedef lt_ptr lt_find_sym LT_PARAMS((lt_user_data loader_data,
+ lt_module handle,
+ const char *symbol));
+typedef int lt_dlloader_exit LT_PARAMS((lt_user_data loader_data));
+
+struct lt_user_dlloader {
+ const char *sym_prefix;
+ lt_module_open *module_open;
+ lt_module_close *module_close;
+ lt_find_sym *find_sym;
+ lt_dlloader_exit *dlloader_exit;
+ lt_user_data dlloader_data;
+};
+
+extern lt_dlloader *lt_dlloader_next LT_PARAMS((lt_dlloader *place));
+extern lt_dlloader *lt_dlloader_find LT_PARAMS((
+ const char *loader_name));
+extern const char *lt_dlloader_name LT_PARAMS((lt_dlloader *place));
+extern lt_user_data *lt_dlloader_data LT_PARAMS((lt_dlloader *place));
+extern int lt_dlloader_add LT_PARAMS((lt_dlloader *place,
+ const struct lt_user_dlloader *dlloader,
+ const char *loader_name));
+extern int lt_dlloader_remove LT_PARAMS((
+ const char *loader_name));
+
+
+
+/* --- ERROR MESSAGE HANDLING --- */
+
+/* Bryce rewrote the error table in a way that would be likely to work
+ on all compilers. VC++ was not able to handle it the way it was
+ done originally. */
+
+/* ORIG COMMENT: Defining error strings alongside their symbolic names in a
+ macro in this way allows us to expand the macro in different contexts with
+ confidence that the enumeration of symbolic names will map correctly
+ onto the table of error strings. */
+
+#define lt_dlerror_symbols_list \
+ LT_ERROR_UNKNOWN, \
+ LT_ERROR_DLOPEN_NOT_SUPPORTED, \
+ LT_ERROR_INVALID_LOADER, \
+ LT_ERROR_INIT_LOADER, \
+ LT_ERROR_REMOVE_LOADER, \
+ LT_ERROR_FILE_NOT_FOUND, \
+ LT_ERROR_DEPLIB_NOT_FOUND, \
+ LT_ERROR_NO_SYMBOLS, \
+ LT_ERROR_CANNOT_OPEN, \
+ LT_ERROR_CANNOT_CLOSE, \
+ LT_ERROR_SYMBOL_NOT_FOUND, \
+ LT_ERROR_NO_MEMORY, \
+ LT_ERROR_INVALID_HANDLE, \
+ LT_ERROR_BUFFER_OVERFLOW, \
+ LT_ERROR_INVALID_ERRORCODE, \
+ LT_ERROR_SHUTDOWN, \
+ LT_ERROR_CLOSE_RESIDENT_MODULE, \
+ LT_ERROR_INVALID_MUTEX_ARGS, \
+ LT_ERROR_INVALID_POSITION,
+
+#define lt_dlerror_names_list \
+ "unknown error", \
+ "dlopen support not available", \
+ "invalid loader", \
+ "loader initialization failed", \
+ "loader removal failed", \
+ "file not found", \
+ "dependency library not found", \
+ "no symbols defined", \
+ "can't open the module", \
+ "can't close the module", \
+ "symbol not found", \
+ "not enough memory", \
+ "invalid module handle", \
+ "internal buffer overflow", \
+ "invalid errorcode", \
+ "library already shutdown", \
+ "can't close resident module", \
+ "invalid mutex handler registration", \
+ "invalid search path insert position",
+
+/* Enumerate the symbolic error names. */
+enum {
+ lt_dlerror_symbols_list
+ LT_ERROR_MAX
+};
+
+/* These functions are only useful from inside custom module loaders. */
+extern int lt_dladderror LT_PARAMS((const char *diagnostic));
+extern int lt_dlseterror LT_PARAMS((int errorcode));
+
+
+
+
+/* --- SOURCE COMPATIBILITY WITH OLD LIBLTDL --- */
+
+
+#ifdef LT_NON_POSIX_NAMESPACE
+# define lt_ptr_t lt_ptr
+# define lt_module_t lt_module
+# define lt_module_open_t lt_module_open
+# define lt_module_close_t lt_module_close
+# define lt_find_sym_t lt_find_sym
+# define lt_dlloader_exit_t lt_dlloader_exit
+# define lt_dlloader_t lt_dlloader
+# define lt_dlloader_data_t lt_user_data
+#endif
+
+LT_END_C_DECLS
+
+#endif /* !LTDL_H */
diff --git a/tools/ioemu/include/ltdlconf.h b/tools/ioemu/include/ltdlconf.h
new file mode 100644
index 0000000000..5ffd0e7916
--- /dev/null
+++ b/tools/ioemu/include/ltdlconf.h
@@ -0,0 +1,161 @@
+/* ltdlconf.h. Generated by configure. */
+/////////////////////////////////////////////////////////////////////////
+// $Id: ltdlconf.h.in,v 1.2 2002/10/24 21:04:38 bdenney Exp $
+//
+// The configure script reads this file and produces ltdlconf.h, which
+// tells ltdl.c how to compile. It was copied out of the libtool package
+// but appears to have been generated by autoheader.
+/////////////////////////////////////////////////////////////////////////
+
+/* config-h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define to empty if the keyword does not work. */
+/* #undef const */
+
+/* Define as __inline if that's what the C compiler calls it. */
+/* #undef inline */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you have the argz_append function. */
+#define HAVE_ARGZ_APPEND 1
+
+/* Define if you have the argz_create_sep function. */
+#define HAVE_ARGZ_CREATE_SEP 1
+
+/* Define if you have the argz_insert function. */
+#define HAVE_ARGZ_INSERT 1
+
+/* Define if you have the argz_next function. */
+#define HAVE_ARGZ_NEXT 1
+
+/* Define if you have the argz_stringify function. */
+#define HAVE_ARGZ_STRINGIFY 1
+
+/* Define if you have the bcopy function. */
+/* #undef HAVE_BCOPY */
+
+/* Define if you have the dlerror function. */
+#define HAVE_DLERROR 1
+
+/* Define if you have the index function. */
+/* #undef HAVE_INDEX */
+
+/* Define if you have the memcpy function. */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the memmove function. */
+#define HAVE_MEMMOVE 1
+
+/* Define if you have the rindex function. */
+/* #undef HAVE_RINDEX */
+
+/* Define if you have the strchr function. */
+#define HAVE_STRCHR 1
+
+/* Define if you have the strcmp function. */
+#define HAVE_STRCMP 1
+
+/* Define if you have the strrchr function. */
+#define HAVE_STRRCHR 1
+
+/* Define if you have the <argz.h> header file. */
+#define HAVE_ARGZ_H 1
+
+/* Define if you have the <assert.h> header file. */
+#define HAVE_ASSERT_H 1
+
+/* Define if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the <dl.h> header file. */
+/* #undef HAVE_DL_H */
+
+/* Define if you have the <dld.h> header file. */
+/* #undef HAVE_DLD_H */
+
+/* Define if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the <ndir.h> header file. */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define if you have the <sys/dir.h> header file. */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the <sys/dl.h> header file. */
+/* #undef HAVE_SYS_DL_H */
+
+/* Define if you have the <sys/ndir.h> header file. */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the extension used for shared libraries, say, .so. */
+#define LTDL_SHLIB_EXT ".so"
+
+/* Define to the name of the environment variable that determines the dynamic library search path. */
+#define LTDL_SHLIBPATH_VAR "LD_LIBRARY_PATH"
+
+/* Define to the system default library search path. */
+#define LTDL_SYSSEARCHPATH "/lib:/usr/lib"
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries. */
+#define LTDL_OBJDIR ".libs/"
+
+/* Define if libtool can extract symbol lists from object files. */
+#define HAVE_PRELOADED_SYMBOLS 1
+
+/* Define if you have the libdl library or equivalent. */
+#define HAVE_LIBDL 1
+
+/* Define if you have the libdl library or equivalent. */
+#define HAVE_LIBDL 1
+
+/* Define if you have the libdl library or equivalent. */
+#define HAVE_LIBDL 1
+
+/* Define if you have the shl_load function. */
+/* #undef HAVE_SHL_LOAD */
+
+/* Define if you have the shl_load function. */
+/* #undef HAVE_SHL_LOAD */
+
+/* Define if you have the GNU dld library. */
+/* #undef HAVE_DLD */
+
+/* Define if dlsym() requires a leading underscode in symbol names. */
+/* #undef NEED_USCORE */
+
+/* Define if the OS needs help to load dependent libraries for dlopen(). */
+/* #undef LTDL_DLOPEN_DEPLIBS */
+
+/* Define to a type to use for `error_t' if it is not otherwise available. */
+/* #undef error_t */
+
diff --git a/tools/ioemu/include/osdep.h b/tools/ioemu/include/osdep.h
new file mode 100644
index 0000000000..a47b88d06e
--- /dev/null
+++ b/tools/ioemu/include/osdep.h
@@ -0,0 +1,176 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: osdep.h,v 1.19 2003/08/20 06:26:27 japj Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// osdep.h
+//
+// requires Bit32u/Bit64u from config.h, size_t from stdio.h
+//
+// Operating system dependent includes and defines for Bochs. These
+// declarations can be included by C or C++., but they require definition of
+// size_t beforehand. This makes it difficult to place them into either
+// config.h or bochs.h. If in config.h, size_t is not always available yet.
+// If in bochs.h, they can't be included by C programs so they lose.
+//
+
+#ifndef BX_OSDEP_H
+#define BX_OSDEP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+//////////////////////////////////////////////////////////////////////
+// Hacks for win32, but exclude MINGW32 because it doesn't need them.
+//////////////////////////////////////////////////////////////////////
+#ifdef WIN32
+
+// Definitions that are needed for all WIN32 compilers.
+# define ssize_t long
+
+#ifndef __MINGW32__
+#define FMT_LL "%I64"
+
+// Definitions that are needed for WIN32 compilers EXCEPT FOR
+// cygwin compiling with -mno-cygwin. e.g. VC++.
+
+// always return regular file.
+# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+
+// win32 has snprintf though with different name.
+#define snprintf _snprintf
+#else /* ifnndef __MINGW32__ */
+#define FMT_LL "%ll"
+#endif /* ifnndef __MINGW32__ */
+#else /* WIN32 */
+#define FMT_LL "%ll"
+#endif /* WIN32 */
+
+// Missing defines for open
+#ifndef S_IRUSR
+#define S_IRUSR 0400
+#define S_IWUSR 0200
+#endif
+#ifndef S_IRGRP
+#define S_IRGRP 0040
+#define S_IWGRP 0020
+#endif
+#ifndef S_IROTH
+#define S_IROTH 0004
+#define S_IWOTH 0002
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Missing library functions.
+// These should work on any platform that needs them.
+//
+// A missing library function is renamed to a bx_* function, so that when
+// debugging and linking there's no confusion over which version is used.
+// Because of renaming, the bx_* replacement functions can be tested on
+// machines which have the real library function without duplicate symbols.
+//
+// If you're considering implementing a missing library function, note
+// that it might be cleaner to conditionally disable the function call!
+//////////////////////////////////////////////////////////////////////
+
+#if !BX_HAVE_SNPRINTF
+#define snprintf bx_snprintf
+ extern int bx_snprintf (char *s, size_t maxlen, const char *format, ...);
+#endif
+
+#if BX_HAVE_STRTOULL
+ // great, just use the usual function
+#elif BX_HAVE_STRTOUQ
+ // they have strtouq and not strtoull
+ #define strtoull strtouq
+#else
+ #define strtoull bx_strtoull
+ extern Bit64u bx_strtoull (const char *nptr, char **endptr, int baseignore);
+#endif
+
+#if !BX_HAVE_STRDUP
+#define strdup bx_strdup
+ extern char *bx_strdup(const char *str);
+#endif
+
+#if !BX_HAVE_STRREV
+#define strrev bx_strrev
+ extern char *bx_strrev(char *str);
+#endif
+
+#if !BX_HAVE_SOCKLEN_T
+// needed on MacOS X 10.1
+typedef int socklen_t;
+#endif
+
+#if !BX_HAVE_MKSTEMP
+#define mkstemp bx_mkstemp
+ extern int bx_mkstemp(char *tpl);
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Missing library functions, implemented for MacOS only
+//////////////////////////////////////////////////////////////////////
+
+#if BX_WITH_MACOS
+// fd_read and fd_write are called by floppy.cc to access the Mac
+// floppy drive directly, since the MacOS doesn't have "special"
+// pathnames which map directly to IO devices
+
+int fd_read(char *buffer, Bit32u offset, Bit32u bytes);
+int fd_write(char *buffer, Bit32u offset, Bit32u bytes);
+int fd_stat(struct stat *buf);
+FILE * fdopen(int fd, const char *type);
+
+typedef long ssize_t ;
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// New functions to replace library functions
+// with OS-independent versions
+//////////////////////////////////////////////////////////////////////
+
+#if BX_HAVE_REALTIME_USEC
+// 64-bit time in useconds.
+extern Bit64u bx_get_realtime64_usec (void);
+#endif
+
+#ifdef WIN32
+#undef BX_HAVE_MSLEEP
+#define BX_HAVE_MSLEEP 1
+#ifndef __MINGW32__
+#define msleep(msec) _sleep(msec)
+#else
+#define msleep(msec) Sleep(msec)
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* ifdef BX_OSDEP_H */
diff --git a/tools/ioemu/include/pc_system.h b/tools/ioemu/include/pc_system.h
new file mode 100644
index 0000000000..c8ea664577
--- /dev/null
+++ b/tools/ioemu/include/pc_system.h
@@ -0,0 +1,226 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pc_system.h,v 1.25 2003/03/02 23:59:08 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+#define BX_MAX_TIMERS 64
+#define BX_NULL_TIMER_HANDLE 10000
+
+
+#if BX_SHOW_IPS
+extern unsigned long ips_count;
+#endif
+
+
+typedef void (*bx_timer_handler_t)(void *);
+
+
+BOCHSAPI extern class bx_pc_system_c bx_pc_system;
+
+#ifdef PROVIDE_M_IPS
+extern double m_ips;
+#endif
+
+#ifdef BX_USE_VMX
+extern unsigned int tsc_per_bx_tick;
+
+#define rdtscll(val) \
+ __asm__ __volatile__("rdtsc" : "=A" (val))
+#endif
+
+class BOCHSAPI bx_pc_system_c : private logfunctions {
+private:
+
+ // ===============================
+ // Timer oriented private features
+ // ===============================
+
+ struct {
+ bx_bool inUse; // Timer slot is in-use (currently registered).
+ Bit64u period; // Timer periodocity in cpu ticks.
+ Bit64u timeToFire; // Time to fire next (in absolute ticks).
+ bx_bool active; // 0=inactive, 1=active.
+ bx_bool continuous; // 0=one-shot timer, 1=continuous periodicity.
+ bx_timer_handler_t funct; // A callback function for when the
+ // timer fires.
+ void *this_ptr; // The this-> pointer for C++ callbacks
+ // has to be stored as well.
+#define BxMaxTimerIDLen 32
+ char id[BxMaxTimerIDLen]; // String ID of timer.
+ } timer[BX_MAX_TIMERS];
+
+ unsigned numTimers; // Number of currently allocated timers.
+ Bit32u currCountdown; // Current countdown ticks value (decrements to 0).
+ Bit32u currCountdownPeriod; // Length of current countdown period.
+ Bit64u ticksTotal; // Num ticks total since start of emulator execution.
+ Bit64u lastTimeUsec; // Last sequentially read time in usec.
+ Bit64u usecSinceLast; // Number of useconds claimed since then.
+
+ // A special null timer is always inserted in the timer[0] slot. This
+ // make sure that at least one timer is always active, and that the
+ // duration is always less than a maximum 32-bit integer, so a 32-bit
+ // counter can be used for the current countdown.
+ static const Bit64u NullTimerInterval;
+ static void nullTimer(void* this_ptr);
+
+#if !defined(PROVIDE_M_IPS)
+ // This is the emulator speed, as measured in millions of
+ // x86 instructions per second that it can emulate on some hypothetically
+ // nomimal workload.
+ double m_ips; // Millions of Instructions Per Second
+#endif
+
+#ifdef BX_USE_VMX
+ static Bit64s get_clock(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000LL + tv.tv_usec;
+ }
+
+ static Bit64u cpu_calibrate_ticks(void) {
+ Bit64s usec, t1, t2;
+
+ usec = get_clock();
+ rdtscll(t1);
+
+ usleep(50 * 1000);
+ usec = get_clock() - usec;
+ rdtscll(t2);
+
+ return (((t2 - t1) * 1000000LL + (usec >> 1)) / usec);
+ }
+#endif
+ // This handler is called when the function which decrements the clock
+ // ticks finds that an event has occurred.
+ void countdownEvent(void);
+
+public:
+
+ // ==============================
+ // Timer oriented public features
+ // ==============================
+
+ void init_ips(Bit32u ips);
+ int register_timer( void *this_ptr, bx_timer_handler_t, Bit32u useconds,
+ bx_bool continuous, bx_bool active, const char *id);
+ unsigned unregisterTimer(int timerID);
+ void start_timers(void);
+ void activate_timer( unsigned timer_index, Bit32u useconds,
+ bx_bool continuous );
+ void deactivate_timer( unsigned timer_index );
+ static BX_CPP_INLINE void tick1(void) {
+#if BX_SHOW_IPS
+ {
+ extern unsigned long ips_count;
+ ips_count++;
+ }
+#endif
+ if (--bx_pc_system.currCountdown == 0) {
+ bx_pc_system.countdownEvent();
+ }
+ }
+ static BX_CPP_INLINE void tickn(Bit64u n) {
+#if BX_SHOW_IPS
+ {
+ extern unsigned long ips_count;
+ ips_count += n;
+ }
+#endif
+ while (n >= Bit64u(bx_pc_system.currCountdown)) {
+ n -= Bit64u(bx_pc_system.currCountdown);
+ bx_pc_system.currCountdown = 0;
+ bx_pc_system.countdownEvent();
+ // bx_pc_system.currCountdown is adjusted to new value by countdownevent().
+ };
+ // 'n' is not (or no longer) >= the countdown size. We can just decrement
+ // the remaining requested ticks and continue.
+ bx_pc_system.currCountdown -= Bit32u(n);
+ }
+
+ int register_timer_ticks(void* this_ptr, bx_timer_handler_t, Bit64u ticks,
+ bx_bool continuous, bx_bool active, const char *id);
+ void activate_timer_ticks(unsigned index, Bit64u instructions,
+ bx_bool continuous);
+ Bit64u time_usec();
+ Bit64u time_usec_sequential();
+ static BX_CPP_INLINE Bit64u time_ticks() {
+ return bx_pc_system.ticksTotal +
+ Bit64u(bx_pc_system.currCountdownPeriod - bx_pc_system.currCountdown);
+ }
+ static BX_CPP_INLINE Bit64u getTicksTotal(void) {
+ return bx_pc_system.ticksTotal;
+ }
+
+ static BX_CPP_INLINE Bit32u getNumCpuTicksLeftNextEvent(void) {
+ return bx_pc_system.currCountdown;
+ }
+#if BX_DEBUGGER
+ static void timebp_handler(void* this_ptr);
+#endif
+
+
+ // ===========================
+ // Non-timer oriented features
+ // ===========================
+
+ bx_bool HRQ; // Hold Request
+ //bx_bool INTR; // Interrupt
+
+
+ // Address line 20 control:
+ // 1 = enabled: extended memory is accessible
+ // 0 = disabled: A20 address line is forced low to simulate
+ // an 8088 address map
+ bx_bool enable_a20;
+
+ // start out masking physical memory addresses to:
+ // 8086: 20 bits
+ // 286: 24 bits
+ // 386: 32 bits
+ // when A20 line is disabled, mask physical memory addresses to:
+ // 286: 20 bits
+ // 386: 20 bits
+ //
+ Bit32u a20_mask;
+
+ void set_HRQ(bx_bool val); // set the Hold ReQuest line
+ void set_INTR(bx_bool value); // set the INTR line to value
+
+ int IntEnabled( void );
+ int InterruptSignal( PCS_OP operation );
+ int ResetSignal( PCS_OP operation );
+ Bit8u IAC(void);
+
+ bx_pc_system_c(void);
+
+ Bit32u inp(Bit16u addr, unsigned io_len) BX_CPP_AttrRegparmN(2);
+ void outp(Bit16u addr, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3);
+ void set_enable_a20(Bit8u value) BX_CPP_AttrRegparmN(1);
+ bx_bool get_enable_a20(void);
+ void exit(void);
+
+ };
diff --git a/tools/ioemu/include/plugin.h b/tools/ioemu/include/plugin.h
new file mode 100644
index 0000000000..dabd13fdcc
--- /dev/null
+++ b/tools/ioemu/include/plugin.h
@@ -0,0 +1,323 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: plugin.h,v 1.20 2003/08/04 16:03:08 akrisak Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// This file provides macros and types needed for plugins. It is based on
+// the plugin.h file from plex86, but with significant changes to make
+// it work in Bochs.
+// Plex86 is Copyright (C) 1999-2000 The plex86 developers team
+//
+/////////////////////////////////////////////////////////////////////////
+
+#ifndef __PLUGIN_H
+#define __PLUGIN_H
+
+#include "extplugin.h"
+
+class bx_devices_c;
+BOCHSAPI extern logfunctions *pluginlog;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BX_PLUGIN_UNMAPPED "unmapped"
+#define BX_PLUGIN_BIOSDEV " biosdev"
+#define BX_PLUGIN_CMOS "cmos"
+#define BX_PLUGIN_VGA "vga"
+#define BX_PLUGIN_FLOPPY "floppy"
+#define BX_PLUGIN_PARALLEL "parallel"
+#define BX_PLUGIN_SERIAL "serial"
+#define BX_PLUGIN_KEYBOARD "keyboard"
+#define BX_PLUGIN_HARDDRV "harddrv"
+#define BX_PLUGIN_DMA "dma"
+#define BX_PLUGIN_PIC "pic"
+#define BX_PLUGIN_PCI "pci"
+#define BX_PLUGIN_PCI2ISA "pci2isa"
+#define BX_PLUGIN_SB16 "sb16"
+#define BX_PLUGIN_NE2K "ne2k"
+#define BX_PLUGIN_EXTFPUIRQ "extfpuirq"
+#define BX_PLUGIN_PCIVGA "pcivga"
+#define BX_PLUGIN_PCIUSB "pciusb"
+#define BX_PLUGIN_GAMEPORT "gameport"
+
+
+#define BX_REGISTER_DEVICE_DEVMODEL(a,b,c,d) pluginRegisterDeviceDevmodel(a,b,c,d)
+
+#if BX_PLUGINS
+
+#define DEV_init_devices() {bx_devices.init(BX_MEM(0)); }
+#define DEV_reset_devices(type) {bx_devices.reset(type); }
+#define PLUG_load_plugin(name,type) {bx_load_plugin(#name,type);}
+
+#define DEV_register_ioread_handler(b,c,d,e,f) pluginRegisterIOReadHandler(b,c,d,e,f)
+#define DEV_register_iowrite_handler(b,c,d,e,f) pluginRegisterIOWriteHandler(b,c,d,e,f)
+#define DEV_register_default_ioread_handler(b,c,d,e) pluginRegisterDefaultIOReadHandler(b,c,d,e)
+#define DEV_register_default_iowrite_handler(b,c,d,e) pluginRegisterDefaultIOWriteHandler(b,c,d,e)
+
+#define DEV_register_irq(b,c) pluginRegisterIRQ(b,c)
+#define DEV_unregister_irq(b,c) pluginUnregisterIRQ(b,c)
+
+#else
+
+#define DEV_init_devices() {bx_devices.init(BX_MEM(0)); }
+#define DEV_reset_devices(type) {bx_devices.reset(type); }
+// When plugins are off, PLUG_load_plugin will call the plugin_init function
+// directly.
+#define PLUG_load_plugin(name,type) {lib##name##_LTX_plugin_init(NULL,type,0,NULL);}
+#define DEV_register_ioread_handler(b,c,d,e,f) bx_devices.register_io_read_handler(b,c,d,e,f)
+#define DEV_register_iowrite_handler(b,c,d,e,f) bx_devices.register_io_write_handler(b,c,d,e,f)
+#define DEV_register_default_ioread_handler(b,c,d,e) bx_devices.register_default_io_read_handler(b,c,d,e)
+#define DEV_register_default_iowrite_handler(b,c,d,e) bx_devices.register_default_io_write_handler(b,c,d,e)
+#define DEV_register_irq(b,c) bx_devices.register_irq(b,c)
+#define DEV_unregister_irq(b,c) bx_devices.unregister_irq(b,c)
+
+#endif // #if BX_PLUGINS
+
+#define DEV_ioapic_present() (bx_devices.ioapic != NULL)
+
+// FIXME Do we really need pluginRegisterTimer ?
+#define DEV_register_timer(a,b,c,d,e,f) bx_pc_system.register_timer(a,b,c,d,e,f)
+
+///////// CMOS macros
+#define DEV_cmos_get_reg(a) (bx_devices.pluginCmosDevice->get_reg(a))
+#define DEV_cmos_set_reg(a,b) (bx_devices.pluginCmosDevice->set_reg(a,b))
+#define DEV_cmos_checksum() (bx_devices.pluginCmosDevice->checksum_cmos())
+#define DEV_cmos_get_timeval() (bx_devices.pluginCmosDevice->get_timeval())
+
+///////// keyboard macros
+#define DEV_mouse_motion(dx, dy, state) \
+ (bx_devices.pluginKeyboard->mouse_motion(dx, dy, state))
+#define DEV_kbd_gen_scancode(key) \
+ (bx_devices.pluginKeyboard->gen_scancode(key))
+#define DEV_kbd_paste_bytes(bytes, count) \
+ (bx_devices.pluginKeyboard->paste_bytes(bytes,count))
+#define DEV_kbd_paste_delay_changed() \
+ (bx_devices.pluginKeyboard->paste_delay_changed())
+#define DEV_mouse_enabled_changed(val) \
+ (bx_devices.pluginKeyboard->mouse_enabled_changed(val))
+
+///////// hard drive macros
+#define DEV_hd_read_handler(a, b, c) \
+ (bx_devices.pluginHardDrive->virt_read_handler(b, c))
+#define DEV_hd_write_handler(a, b, c, d) \
+ (bx_devices.pluginHardDrive->virt_write_handler(b, c, d))
+#define DEV_hd_get_first_cd_handle() \
+ (bx_devices.pluginHardDrive->get_first_cd_handle())
+#define DEV_hd_get_device_handle(a,b) \
+ (bx_devices.pluginHardDrive->get_device_handle(a,b))
+#define DEV_hd_get_cd_media_status(handle) \
+ (bx_devices.pluginHardDrive->get_cd_media_status(handle))
+#define DEV_hd_set_cd_media_status(handle, status) \
+ (bx_devices.pluginHardDrive->set_cd_media_status(handle, status))
+#define DEV_hd_close_harddrive() bx_devices.pluginHardDrive->close_harddrive()
+#define DEV_hd_present() (bx_devices.pluginHardDrive != &bx_devices.stubHardDrive)
+
+#define DEV_bulk_io_quantum_requested() (bx_devices.bulkIOQuantumsRequested)
+#define DEV_bulk_io_quantum_transferred() (bx_devices.bulkIOQuantumsTransferred)
+#define DEV_bulk_io_host_addr() (bx_devices.bulkIOHostAddr)
+
+///////// FLOPPY macros
+#define DEV_floppy_get_media_status(drive) bx_devices.pluginFloppyDevice->get_media_status(drive)
+#define DEV_floppy_set_media_status(drive, status) bx_devices.pluginFloppyDevice->set_media_status(drive, status)
+#define DEV_floppy_present() (bx_devices.pluginFloppyDevice != &bx_devices.stubFloppy)
+
+///////// DMA macros
+#define DEV_dma_register_8bit_channel(channel, dmaRead, dmaWrite, name) \
+ (bx_devices.pluginDmaDevice->registerDMA8Channel(channel, dmaRead, dmaWrite, name))
+#define DEV_dma_register_16bit_channel(channel, dmaRead, dmaWrite, name) \
+ (bx_devices.pluginDmaDevice->registerDMA16Channel(channel, dmaRead, dmaWrite, name))
+#define DEV_dma_unregister_channel(channel) \
+ (bx_devices.pluginDmaDevice->unregisterDMAChannel(channel))
+#define DEV_dma_set_drq(channel, val) \
+ (bx_devices.pluginDmaDevice->set_DRQ(channel, val))
+#define DEV_dma_get_tc() \
+ (bx_devices.pluginDmaDevice->get_TC())
+#define DEV_dma_raise_hlda() \
+ (bx_devices.pluginDmaDevice->raise_HLDA())
+
+///////// PIC macros
+#define DEV_pic_lower_irq(b) (bx_devices.pluginPicDevice->lower_irq(b))
+#define DEV_pic_raise_irq(b) (bx_devices.pluginPicDevice->raise_irq(b))
+#define DEV_pic_iac() (bx_devices.pluginPicDevice->IAC())
+#define DEV_pic_show_pic_state() (bx_devices.pluginPicDevice->show_pic_state())
+
+///////// VGA macros
+#define DEV_vga_mem_read(addr) (bx_devices.pluginVgaDevice->mem_read(addr))
+#define DEV_vga_mem_write(addr, val) (bx_devices.pluginVgaDevice->mem_write(addr, val))
+#define DEV_vga_redraw_area(left, top, right, bottom) \
+ (bx_devices.pluginVgaDevice->redraw_area(left, top, right, bottom))
+#define DEV_vga_get_text_snapshot(rawsnap, height, width) \
+ (bx_devices.pluginVgaDevice->get_text_snapshot(rawsnap, height, width))
+#define DEV_vga_refresh() \
+ (bx_devices.pluginVgaDevice->trigger_timer(bx_devices.pluginVgaDevice))
+#define DEV_vga_set_update_interval(val) \
+ (bx_devices.pluginVgaDevice->set_update_interval(val))
+#define DEV_vga_get_actl_pal_idx(index) (bx_devices.pluginVgaDevice->get_actl_palette_idx(index))
+
+///////// PCI macros
+#define DEV_register_pci_handlers(b,c,d,e,f) \
+ (bx_devices.pluginPciBridge->register_pci_handlers(b,c,d,e,f))
+#define DEV_pci_rd_memtype(addr) bx_devices.pluginPciBridge->rd_memType(addr)
+#define DEV_pci_wr_memtype(addr) bx_devices.pluginPciBridge->wr_memType(addr)
+#define DEV_pci_print_i440fx_state() bx_devices.pluginPciBridge->print_i440fx_state()
+
+///////// NE2000 macro
+#define DEV_ne2k_print_info(file,page,reg,brief) \
+ bx_devices.pluginNE2kDevice->print_info(file,page,reg,brief)
+
+
+#if BX_HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+typedef Bit32u (*ioReadHandler_t)(void *, Bit32u, unsigned);
+typedef void (*ioWriteHandler_t)(void *, Bit32u, Bit32u, unsigned);
+
+extern plugin_t *plugins;
+
+typedef struct _device_t
+{
+ const char *name;
+ plugin_t *plugin;
+ void (*device_init_mem)(BX_MEM_C *);
+ void (*device_init_dev)();
+ void (*device_reset)(unsigned);
+ void (*device_load_state)();
+ void (*device_save_state)();
+
+ int use_devmodel_interface; // BBD hack
+ class bx_devmodel_c *devmodel; // BBD hack
+
+ struct _device_t *next;
+} device_t;
+
+
+extern device_t *devices;
+
+void plugin_startup (void);
+void plugin_load (char *name, char *args, plugintype_t);
+plugin_t *plugin_unload (plugin_t *plugin);
+void plugin_init_all (void);
+void plugin_fini_all (void);
+
+/* === Device Stuff === */
+typedef void (*deviceInitMem_t)(BX_MEM_C *);
+typedef void (*deviceInitDev_t)(void);
+typedef void (*deviceReset_t)(unsigned);
+typedef void (*deviceLoad_t)(void);
+typedef void (*deviceSave_t)(void);
+
+BOCHSAPI void pluginRegisterDeviceDevmodel(plugin_t *plugin, plugintype_t type, bx_devmodel_c *dev, char *name);
+BOCHSAPI bx_bool pluginDevicePresent(char *name);
+
+/* === IO port stuff === */
+BOCHSAPI extern int (*pluginRegisterIOReadHandler)(void *thisPtr, ioReadHandler_t callback,
+ unsigned base, const char *name, Bit8u mask);
+BOCHSAPI extern int (*pluginRegisterIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback,
+ unsigned base, const char *name, Bit8u mask);
+BOCHSAPI extern int (*pluginRegisterDefaultIOReadHandler)(void *thisPtr, ioReadHandler_t callback,
+ const char *name, Bit8u mask);
+BOCHSAPI extern int (*pluginRegisterDefaultIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback,
+ const char *name, Bit8u mask);
+
+/* === A20 enable line stuff === */
+BOCHSAPI extern unsigned (*pluginGetA20E)(void);
+BOCHSAPI extern void (*pluginSetA20E)(unsigned val);
+
+/* === IRQ stuff === */
+BOCHSAPI extern void (*pluginRegisterIRQ)(unsigned irq, const char *name);
+BOCHSAPI extern void (*pluginUnregisterIRQ)(unsigned irq, const char *name);
+
+/* === Floppy stuff ===*/
+BOCHSAPI extern unsigned (* pluginFloppyGetMediaStatus)(unsigned drive);
+BOCHSAPI extern unsigned (* pluginFloppySetMediaStatus)(unsigned drive, unsigned status);
+
+/* === VGA stuff === */
+BOCHSAPI extern void (* pluginVGARedrawArea)(unsigned x0, unsigned y0,
+ unsigned width, unsigned height);
+BOCHSAPI extern Bit8u (* pluginVGAMemRead)(Bit32u addr);
+BOCHSAPI extern void (* pluginVGAMemWrite)(Bit32u addr, Bit8u value);
+BOCHSAPI extern void (* pluginVGAGetTextSnapshot)(Bit8u **text_snapshot,
+ unsigned *txHeight, unsigned *txWidth);
+BOCHSAPI extern void (* pluginVGARefresh)(void *);
+BOCHSAPI extern void (* pluginVGASetUpdateInterval)(unsigned);
+BOCHSAPI extern Bit8u (* pluginVGAGetActlPaletteIdx)(Bit8u index);
+
+/* === Timer stuff === */
+BOCHSAPI extern int (*pluginRegisterTimer)(void *this_ptr, void (*funct)(void *),
+ Bit32u useconds, bx_bool continuous,
+ bx_bool active, const char *name);
+
+BOCHSAPI extern void (*pluginActivateTimer)(unsigned id, Bit32u usec, bx_bool continuous);
+BOCHSAPI extern void (*pluginDeactivateTimer)(unsigned id);
+
+/* === HRQ stuff === */
+BOCHSAPI extern void (*pluginSetHRQ)(unsigned val);
+BOCHSAPI extern void (*pluginSetHRQHackCallback)( void (*callback)(void) );
+
+/* === Reset stuff === */
+BOCHSAPI extern void (*pluginResetSignal)(unsigned sig);
+
+/* === PCI stuff === */
+BOCHSAPI extern bx_bool (*pluginRegisterPCIDevice)(void *this_ptr,
+ Bit32u (*bx_pci_read_handler)(void *, Bit8u, unsigned),
+ void(*bx_pci_write_handler)(void *, Bit8u, Bit32u, unsigned),
+ Bit8u devfunc, const char *name);
+BOCHSAPI extern Bit8u (*pluginRd_memType)(Bit32u addr);
+BOCHSAPI extern Bit8u (*pluginWr_memType)(Bit32u addr);
+
+void plugin_abort (void);
+
+int bx_load_plugin (const char *name, plugintype_t type);
+extern void bx_init_plugins (void);
+extern void bx_reset_plugins (unsigned);
+
+// every plugin must define these, within the extern"C" block, so that
+// a non-mangled function symbol is available in the shared library.
+void plugin_fini(void);
+int plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]);
+
+// still in extern "C"
+#define DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(mod) \
+ int lib##mod##_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[]); \
+ void lib##mod##_LTX_plugin_fini(void);
+
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(harddrv)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(keyboard)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(serial)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(unmapped)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(biosdev)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(cmos)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(dma)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(pic)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(vga)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(floppy)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(parallel)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(pci)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(pci2isa)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(pcivga)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(pciusb)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(sb16)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(ne2k)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(extfpuirq)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(gameport)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(amigaos)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(beos)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(carbon)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(macintosh)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(nogui)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(rfb)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(sdl)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(svga)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(term)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(win32)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(wx)
+DECLARE_PLUGIN_INIT_FINI_FOR_MODULE(x)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __PLUGIN_H */
diff --git a/tools/ioemu/include/state_file.h b/tools/ioemu/include/state_file.h
new file mode 100644
index 0000000000..7cef477043
--- /dev/null
+++ b/tools/ioemu/include/state_file.h
@@ -0,0 +1,61 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: state_file.h,v 1.5 2002/10/24 21:05:00 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Classes for helping to make checkpoints of the emulator state.
+
+#ifndef _STATE_FILE_H
+#define _STATE_FILE_H
+#include <stdio.h>
+#include <stddef.h>
+
+
+class BOCHSAPI state_file {
+ void init(void);
+public:
+ FILE *file;
+ class logfunctions *log;
+
+ FILE *get_handle();
+ void write(Bit8u);
+ void write(Bit16u);
+ void write(Bit32u);
+ void write(Bit64u);
+ void write(const void*, size_t);
+ void read(Bit8u &);
+ void read(Bit16u &);
+ void read(Bit32u &);
+ void read(Bit64u &);
+ void read(void *, size_t);
+ void write_check(const char *);
+ void read_check (const char *);
+
+ state_file (const char *name, const char *options);
+ state_file (FILE *f);
+ ~state_file();
+};
+
+#endif // #ifndef _STATE_FILE_H
diff --git a/tools/ioemu/iodev/Makefile b/tools/ioemu/iodev/Makefile
new file mode 100644
index 0000000000..3a43661918
--- /dev/null
+++ b/tools/ioemu/iodev/Makefile
@@ -0,0 +1,15 @@
+TOPDIR= ..
+CXXFLAGS=-I. -I../include -I..
+OBJS=$(patsubst %.cc,%.o,$(wildcard *.cc))
+BXLIBS = ../gui/libgui.a ../memory/libmemory.a
+LDLIBS= $(BXLIBS) -L/usr/X11R6/lib -lX11 -lXpm -lstdc++ -L ../../../tools/libxc -L ../../../tools/libxutil -lxc -lxutil
+
+all: device-model
+
+device-model: $(OBJS) $(BXLIBS)
+ $(LINK.o) $(OBJS) $(LOADLIBES) $(LDLIBS) -o $@
+
+include $(TOPDIR)/mk/helix.mk
+
+install:: all
+ install device-model $(DESTDIR)/usr/sbin
diff --git a/tools/ioemu/iodev/aspi-win32.h b/tools/ioemu/iodev/aspi-win32.h
new file mode 100644
index 0000000000..afa62d1e19
--- /dev/null
+++ b/tools/ioemu/iodev/aspi-win32.h
@@ -0,0 +1,210 @@
+//
+// iodev/aspi-win32.h
+// $Id: aspi-win32.h,v 1.2 2001/06/25 12:52:37 bdenney Exp $
+//
+// This file was copied from cdrecord 1.9 under libscg/scg/aspi-win32.h.
+// The only modification is related to use of the PACKED keyword.
+//
+
+#ifndef __ASPI_WIN32_H_
+#define __ASPI_WIN32_H_
+
+#include <windows.h>
+
+#ifndef PACKED
+// It seems that VC++ has no PACKED keyword but Cygwin does. We can just
+// define PACKED to be empty if it's not already defined by the system
+// headers.
+#define PACKED /* empty */
+#endif
+
+/***************************************************************************
+ ** SCSI MISCELLANEOUS EQUATES
+ ***************************************************************************/
+#define SENSE_LEN 14 /* Default sense buffer length */
+#define SRB_DIR_SCSI 0x00 /* Direction determined by SCSI */
+#define SRB_POSTING 0x01 /* Enable ASPI posting */
+#define SRB_ENABLE_RESIDUAL_COUNT 0x04 /* Enable residual byte count */
+ /* reporting */
+#define SRB_DIR_IN 0x08 /* Transfer from SCSI target to */
+ /* host */
+#define SRB_DIR_OUT 0x10 /* Transfer from host to SCSI */
+ /* target */
+#define SRB_EVENT_NOTIFY 0x40 /* Enable ASPI event notification */
+#define RESIDUAL_COUNT_SUPPORTED 0x02 /* Extended buffer flag */
+#define MAX_SRB_TIMEOUT 1080001u /* 30 hour maximum timeout in sec */
+#define DEFAULT_SRB_TIMEOUT 1080001u /* use max.timeout by default */
+
+/***************************************************************************
+ ** ASPI command definitions
+ ***************************************************************************/
+#define SC_HA_INQUIRY 0x00 /* Host adapter inquiry */
+#define SC_GET_DEV_TYPE 0x01 /* Get device type */
+#define SC_EXEC_SCSI_CMD 0x02 /* Execute SCSI command */
+#define SC_ABORT_SRB 0x03 /* Abort an SRB */
+#define SC_RESET_DEV 0x04 /* SCSI bus device reset */
+#define SC_SET_HA_PARMS 0x05 /* Set HA parameters */
+#define SC_GET_DISK_INFO 0x06 /* Get Disk */
+#define SC_RESCAN_SCSI_BUS 0x07 /* Rebuild SCSI device map */
+#define SC_GETSET_TIMEOUTS 0x08 /* Get/Set target timeouts */
+
+
+/***************************************************************************
+ ** SRB Status
+ ***************************************************************************/
+#define SS_PENDING 0x00 /* SRB being processed */
+#define SS_COMP 0x01 /* SRB completed without error */
+#define SS_ABORTED 0x02 /* SRB aborted */
+#define SS_ABORT_FAIL 0x03 /* Unable to abort SRB */
+#define SS_ERR 0x04 /* SRB completed with error */
+#define SS_INVALID_CMD 0x80 /* Invalid ASPI command */
+#define SS_INVALID_HA 0x81 /* Invalid host adapter number */
+#define SS_NO_DEVICE 0x82 /* SCSI device not installed */
+#define SS_INVALID_SRB 0xE0 /* Invalid parameter set in SRB */
+#define SS_OLD_MANAGER 0xE1 /* ASPI manager doesn't support */
+ /* windows */
+#define SS_BUFFER_ALIGN 0xE1 /* Buffer not aligned (replaces */
+ /* SS_OLD_MANAGER in Win32) */
+#define SS_ILLEGAL_MODE 0xE2 /* Unsupported Windows mode */
+#define SS_NO_ASPI 0xE3 /* No ASPI managers */
+#define SS_FAILED_INIT 0xE4 /* ASPI for windows failed init */
+#define SS_ASPI_IS_BUSY 0xE5 /* No resources available to */
+ /* execute command */
+#define SS_BUFFER_TO_BIG 0xE6 /* Buffer size too big to handle */
+#define SS_BUFFER_TOO_BIG 0xE6 /* Correct spelling of 'too' */
+#define SS_MISMATCHED_COMPONENTS 0xE7 /* The DLLs/EXEs of ASPI don't */
+ /* version check */
+#define SS_NO_ADAPTERS 0xE8 /* No host adapters to manager */
+#define SS_INSUFFICIENT_RESOURCES 0xE9 /* Couldn't allocate resources */
+ /* needed to init */
+#define SS_ASPI_IS_SHUTDOWN 0xEA /* Call came to ASPI after */
+ /* PROCESS_DETACH */
+#define SS_BAD_INSTALL 0xEB /* The DLL or other components */
+ /* are installed wrong */
+
+/***************************************************************************
+ ** Host Adapter Status
+ ***************************************************************************/
+#define HASTAT_OK 0x00 /* No error detected by HA */
+#define HASTAT_SEL_TO 0x11 /* Selection Timeout */
+#define HASTAT_DO_DU 0x12 /* Data overrun/data underrun */
+#define HASTAT_BUS_FREE 0x13 /* Unexpected bus free */
+#define HASTAT_PHASE_ERR 0x14 /* Target bus phase sequence */
+#define HASTAT_TIMEOUT 0x09 /* Timed out while SRB was */
+ /* waiting to be processed */
+#define HASTAT_COMMAND_TIMEOUT 0x0B /* Adapter timed out while */
+ /* processing SRB */
+#define HASTAT_MESSAGE_REJECT 0x0D /* While processing the SRB, the */
+ /* adapter received a MESSAGE */
+#define HASTAT_BUS_RESET 0x0E /* A bus reset was detected */
+#define HASTAT_PARITY_ERROR 0x0F /* A parity error was detected */
+#define HASTAT_REQUEST_SENSE_FAILED 0x10 /* The adapter failed in issuing */
+
+/***************************************************************************
+ ** SRB - HOST ADAPTER INQUIRIY - SC_HA_INQUIRY (0)
+ ***************************************************************************/
+typedef struct {
+ BYTE SRB_Cmd; /* 00/000 ASPI command code == SC_HA_INQUIRY */
+ BYTE SRB_Status; /* 01/001 ASPI command status byte */
+ BYTE SRB_HaId; /* 02/002 ASPI host adapter number */
+ BYTE SRB_Flags; /* 03/003 ASPI request flags */
+ DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */
+ BYTE HA_Count; /* 08/008 Number of host adapters present */
+ BYTE HA_SCSI_ID; /* 09/009 SCSI ID of host adapter */
+ BYTE HA_ManagerId[16]; /* 0a/010 String describing the manager */
+ BYTE HA_Identifier[16]; /* 1a/026 String describing the host adapter */
+ BYTE HA_Unique[16]; /* 2a/042 Host Adapter Unique parameters */
+ WORD HA_Rsvd1; /* 3a/058 Reserved, must = 0 */
+} PACKED SRB_HAInquiry, *PSRB_HAInquiry, FAR *LPSRB_HAInquiry;
+
+
+/***************************************************************************
+ ** SRB - GET DEVICE TYPE - SC_GET_DEV_TYPE (1)
+ ***************************************************************************/
+typedef struct
+{
+ BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_GET_DEV_TYPE */
+ BYTE SRB_Status; /* 01/001 ASPI command status byte */
+ BYTE SRB_HaId; /* 02/002 ASPI host adapter number */
+ BYTE SRB_Flags; /* 03/003 Reserved, must = 0 */
+ DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */
+ BYTE SRB_Target; /* 08/008 Target's SCSI ID */
+ BYTE SRB_Lun; /* 09/009 Target's LUN number */
+ BYTE SRB_DeviceType; /* 0a/010 Target's peripheral device type */
+ BYTE SRB_Rsvd1; /* 0b/011 Reserved, must = 0 */
+} PACKED SRB_GDEVBlock, *PSRB_GDEVBlock, FAR *LPSRB_GDEVBlock;
+
+
+/***************************************************************************
+ ** SRB - EXECUTE SCSI COMMAND - SC_EXEC_SCSI_CMD (2)
+ ***************************************************************************/
+typedef struct
+{
+ BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_EXEC_SCSI_CMD */
+ BYTE SRB_Status; /* 01/001 ASPI command status byte */
+ BYTE SRB_HaId; /* 02/002 ASPI host adapter number */
+ BYTE SRB_Flags; /* 03/003 Reserved, must = 0 */
+ DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */
+ BYTE SRB_Target; /* 08/008 Target's SCSI ID */
+ BYTE SRB_Lun; /* 09/009 Target's LUN */
+ WORD SRB_Rsvd1; /* 0a/010 Reserved for alignment */
+ DWORD SRB_BufLen; /* 0c/012 Data Allocation Length */
+ BYTE FAR *SRB_BufPointer; /* 10/016 Data Buffer Pointer */
+ BYTE SRB_SenseLen; /* 14/020 Sense Allocation Length */
+ BYTE SRB_CDBLen; /* 15/021 CDB Length */
+ BYTE SRB_HaStat; /* 16/022 Host Adapter Status */
+ BYTE SRB_TargStat; /* 17/023 Target Status */
+ VOID FAR *SRB_PostProc; /* 18/024 Post routine */
+ BYTE SRB_Rsvd2[20]; /* 1c/028 Reserved, must = 0 */
+ BYTE CDBByte[16]; /* 30/048 SCSI CDB */
+ BYTE SenseArea[SENSE_LEN+2]; /* 40/064 Request Sense buffer */
+} PACKED SRB_ExecSCSICmd, *PSRB_ExecSCSICmd, FAR *LPSRB_ExecSCSICmd;
+
+
+typedef struct
+{
+ BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_ABORT_SRB */
+ BYTE SRB_Status; /* 01/001 ASPI command status byte */
+ BYTE SRB_HaId; /* 02/002 ASPI host adapter number */
+ BYTE SRB_Flags; /* 03/003 Reserved, must = 0 */
+ DWORD SRB_Hdr_Rsvd; /* 04/004 Reserved, must = 0 */
+ void *SRB_ToAbort; /* 08/008 Pointer to SRB to abort */
+} PACKED SRB_Abort, *PSRB_Abort, FAR *LPSRB_Abort;
+
+
+/***************************************************************************
+ ** SRB - BUS DEVICE RESET - SC_RESET_DEV (4)
+ ***************************************************************************/
+typedef struct
+{
+ BYTE SRB_Cmd; /* 00/000 ASPI cmd code == SC_RESET_DEV */
+ BYTE SRB_Status; /* 01/001 ASPI command status byte */
+ BYTE SRB_HaId; /* 02/002 ASPI host adapter number */
+ DWORD SRB_Flags; /* 04/004 Reserved */
+ BYTE SRB_Target; /* 08/008 Target's SCSI ID */
+ BYTE SRB_Lun; /* 09/009 Target's LUN number */
+ BYTE SRB_Rsvd1[12]; /* 0A/010 Reserved for alignment */
+ BYTE SRB_HaStat; /* 16/022 Host Adapter Status */
+ BYTE SRB_TargStat; /* 17/023 Target Status */
+ VOID FAR *SRB_PostProc; /* 18/024 Post routine */
+ BYTE SRB_Rsvd2[36]; /* 1C/028 Reserved, must = 0 */
+} SRB_BusDeviceReset, *PSRB_BusDeviceReset, FAR *LPSRB_BusDeviceReset;
+
+typedef struct tag_ASPI32BUFF
+{
+ PBYTE AB_BufPointer;
+ DWORD AB_BufLen;
+ DWORD AB_ZeroFill;
+ DWORD AB_Reserved;
+} PACKED ASPI32BUFF, *PASPI32BUFF, FAR *LPASPI32BUFF;
+
+typedef struct
+{
+ BYTE SRB_Cmd;
+ BYTE SRB_Status;
+ BYTE SRB_HaId;
+ BYTE SRB_Flags;
+ DWORD SRB_Hdr_Rsvd;
+} SRB, *PSRB, FAR *LPSRB;
+
+#endif
diff --git a/tools/ioemu/iodev/biosdev.cc b/tools/ioemu/iodev/biosdev.cc
new file mode 100644
index 0000000000..d4a6ef2b8c
--- /dev/null
+++ b/tools/ioemu/iodev/biosdev.cc
@@ -0,0 +1,212 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: biosdev.cc,v 1.7 2003/12/08 19:36:23 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Here are the virtual ports use to display messages from the bioses :
+//
+// 0x0400 : rombios Panic port with message
+// 0x0401 : rombios Panic port with line number
+// 0x0402 : rombios Info port with message
+// 0x0403 : rombios Debug port with message
+//
+// 0x0500 : vgabios Info port with message
+// 0x0501 : vgabios Panic port with message
+// 0x0502 : vgabios Panic port with line number
+// 0x0503 : vgabios Debug port with message
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+bx_biosdev_c *theBiosDevice;
+
+ int
+libbiosdev_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theBiosDevice = new bx_biosdev_c ();
+ bx_devices.pluginBiosDevice = theBiosDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theBiosDevice, BX_PLUGIN_BIOSDEV);
+ return(0); // Success
+}
+
+ void
+libbiosdev_LTX_plugin_fini(void)
+{
+}
+
+logfunctions *bioslog;
+logfunctions *vgabioslog;
+
+bx_biosdev_c::bx_biosdev_c(void)
+{
+ bioslog = new logfunctions();
+ bioslog->put("BIOS");
+ bioslog->settype(BIOSLOG);
+ s.bios_message_i = 0;
+
+ vgabioslog = new logfunctions();
+ vgabioslog->put("VBIOS");
+ vgabioslog->settype(BIOSLOG);
+ s.vgabios_message_i = 0;
+}
+
+bx_biosdev_c::~bx_biosdev_c(void)
+{
+ if ( bioslog != NULL )
+ {
+ delete bioslog;
+ bioslog = NULL;
+ }
+
+ if ( vgabioslog != NULL )
+ {
+ delete vgabioslog;
+ vgabioslog = NULL;
+ }
+}
+
+ void
+bx_biosdev_c::init(void)
+{
+ DEV_register_iowrite_handler(this, write_handler, 0x0400, "Bios Panic Port 1", 3);
+ DEV_register_iowrite_handler(this, write_handler, 0x0401, "Bios Panic Port 2", 3);
+ DEV_register_iowrite_handler(this, write_handler, 0x0403, "Bios Debug Port", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0402, "Bios Info Port", 1);
+
+ DEV_register_iowrite_handler(this, write_handler, 0x0501, "VGABios Panic Port 1", 3);
+ DEV_register_iowrite_handler(this, write_handler, 0x0502, "VGABios Panic Port 2", 3);
+ DEV_register_iowrite_handler(this, write_handler, 0x0503, "VGABios Debug Port", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0500, "VGABios Info Port", 1);
+}
+
+ void
+bx_biosdev_c::reset(unsigned type)
+{
+}
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_biosdev_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_BIOS_SMF
+ bx_biosdev_c *class_ptr = (bx_biosdev_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_biosdev_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_BIOS_SMF
+ UNUSED(io_len);
+
+
+ switch (address) {
+ // 0x400-0x401 are used as panic ports for the rombios
+ case 0x0401:
+ if (BX_BIOS_THIS s.bios_message_i > 0) {
+ // if there are bits of message in the buffer, print them as the
+ // panic message. Otherwise fall into the next case.
+ if (BX_BIOS_THIS s.bios_message_i >= BX_BIOS_MESSAGE_SIZE)
+ BX_BIOS_THIS s.bios_message_i = BX_BIOS_MESSAGE_SIZE-1;
+ BX_BIOS_THIS s.bios_message[ BX_BIOS_THIS s.bios_message_i] = 0;
+ BX_BIOS_THIS s.bios_message_i = 0;
+ bioslog->panic("%s", BX_BIOS_THIS s.bios_message);
+ break;
+ }
+ case 0x0400:
+ bioslog->panic("BIOS panic at rombios.c, line %d", value);
+ break;
+
+ // 0x0402 is used as the info port for the rombios
+ // 0x0403 is used as the debug port for the rombios
+ case 0x0402:
+ case 0x0403:
+ BX_BIOS_THIS s.bios_message[BX_BIOS_THIS s.bios_message_i] =
+ (Bit8u) value;
+ BX_BIOS_THIS s.bios_message_i ++;
+ if ( BX_BIOS_THIS s.bios_message_i >= BX_BIOS_MESSAGE_SIZE ) {
+ BX_BIOS_THIS s.bios_message[ BX_BIOS_MESSAGE_SIZE - 1] = 0;
+ BX_BIOS_THIS s.bios_message_i = 0;
+ if (address==0x403) bioslog->ldebug("%s", BX_BIOS_THIS s.bios_message);
+ else bioslog->info("%s", BX_BIOS_THIS s.bios_message);
+ }
+ else if ((value & 0xff) == '\n') {
+ BX_BIOS_THIS s.bios_message[ BX_BIOS_THIS s.bios_message_i - 1 ] = 0;
+ BX_BIOS_THIS s.bios_message_i = 0;
+ if (address==0x403) bioslog->ldebug("%s", BX_BIOS_THIS s.bios_message);
+ else bioslog->info("%s", BX_BIOS_THIS s.bios_message);
+ }
+ break;
+
+ // 0x501-0x502 are used as panic ports for the vgabios
+ case 0x0502:
+ if (BX_BIOS_THIS s.vgabios_message_i > 0) {
+ // if there are bits of message in the buffer, print them as the
+ // panic message. Otherwise fall into the next case.
+ if (BX_BIOS_THIS s.vgabios_message_i >= BX_BIOS_MESSAGE_SIZE)
+ BX_BIOS_THIS s.vgabios_message_i = BX_BIOS_MESSAGE_SIZE-1;
+ BX_BIOS_THIS s.vgabios_message[ BX_BIOS_THIS s.vgabios_message_i] = 0;
+ BX_BIOS_THIS s.vgabios_message_i = 0;
+ vgabioslog->panic("%s", BX_BIOS_THIS s.vgabios_message);
+ break;
+ }
+ case 0x0501:
+ vgabioslog->panic("BIOS panic at rombios.c, line %d", value);
+ break;
+
+ // 0x0500 is used as the message port for the vgabios
+ case 0x0500:
+ case 0x0503:
+ BX_BIOS_THIS s.vgabios_message[BX_BIOS_THIS s.vgabios_message_i] =
+ (Bit8u) value;
+ BX_BIOS_THIS s.vgabios_message_i ++;
+ if ( BX_BIOS_THIS s.vgabios_message_i >= BX_BIOS_MESSAGE_SIZE ) {
+ BX_BIOS_THIS s.vgabios_message[ BX_BIOS_MESSAGE_SIZE - 1] = 0;
+ BX_BIOS_THIS s.vgabios_message_i = 0;
+ if (address==0x503) vgabioslog->ldebug("%s", BX_BIOS_THIS s.vgabios_message);
+ else vgabioslog->info("%s", BX_BIOS_THIS s.vgabios_message);
+ }
+ else if ((value & 0xff) == '\n') {
+ BX_BIOS_THIS s.vgabios_message[ BX_BIOS_THIS s.vgabios_message_i - 1 ] = 0;
+ BX_BIOS_THIS s.vgabios_message_i = 0;
+ if (address==0x503) vgabioslog->ldebug("%s", BX_BIOS_THIS s.vgabios_message);
+ else vgabioslog->info("%s", BX_BIOS_THIS s.vgabios_message);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
diff --git a/tools/ioemu/iodev/biosdev.h b/tools/ioemu/iodev/biosdev.h
new file mode 100644
index 0000000000..7cd98736f4
--- /dev/null
+++ b/tools/ioemu/iodev/biosdev.h
@@ -0,0 +1,63 @@
+
+// $Id: biosdev.h,v 1.3 2002/10/24 21:07:09 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#define BX_BIOS_MESSAGE_SIZE 80
+
+
+#if BX_USE_BIOS_SMF
+# define BX_BIOS_SMF static
+# define BX_BIOS_THIS theBiosDevice->
+#else
+# define BX_BIOS_SMF
+# define BX_BIOS_THIS this->
+#endif
+
+
+class bx_biosdev_c : public bx_devmodel_c {
+public:
+ bx_biosdev_c(void);
+ ~bx_biosdev_c(void);
+
+ virtual void init(void);
+ virtual void reset (unsigned type);
+
+private:
+
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_BIOS_SMF
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+
+ struct {
+ Bit8u bios_message[BX_BIOS_MESSAGE_SIZE];
+ unsigned int bios_message_i;
+
+ Bit8u vgabios_message[BX_BIOS_MESSAGE_SIZE];
+ unsigned int vgabios_message_i;
+ } s; // state information
+
+ };
diff --git a/tools/ioemu/iodev/cdrom.cc b/tools/ioemu/iodev/cdrom.cc
new file mode 100644
index 0000000000..2b78e8d15a
--- /dev/null
+++ b/tools/ioemu/iodev/cdrom.cc
@@ -0,0 +1,1338 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: cdrom.cc,v 1.66 2003/12/08 23:49:48 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// These are the low-level CDROM functions which are called
+// from 'harddrv.cc'. They effect the OS specific functionality
+// needed by the CDROM emulation in 'harddrv.cc'. Mostly, just
+// ioctl() calls and such. Should be fairly easy to add support
+// for your OS if it is not supported yet.
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_SUPPORT_CDROM
+
+#define LOG_THIS /* no SMF tricks here, not needed */
+
+extern "C" {
+#include <errno.h>
+}
+
+#ifdef __linux__
+extern "C" {
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+// I use the framesize in non OS specific code too
+#define BX_CD_FRAMESIZE CD_FRAMESIZE
+}
+
+#elif defined(__GNU__) || (defined(__CYGWIN32__) && !defined(WIN32))
+extern "C" {
+#include <sys/ioctl.h>
+#define BX_CD_FRAMESIZE 2048
+#define CD_FRAMESIZE 2048
+}
+
+#elif BX_WITH_MACOS
+#define BX_CD_FRAMESIZE 2048
+#define CD_FRAMESIZE 2048
+
+#elif defined(__sun)
+extern "C" {
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+#define BX_CD_FRAMESIZE CDROM_BLK_2048
+}
+
+#elif defined(__DJGPP__)
+extern "C" {
+#include <sys/ioctl.h>
+#define BX_CD_FRAMESIZE 2048
+#define CD_FRAMESIZE 2048
+}
+
+#elif defined(__BEOS__)
+#include "cdrom_beos.h"
+#define BX_CD_FRAMESIZE 2048
+
+#elif (defined (__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__))
+// OpenBSD pre version 2.7 may require extern "C" { } structure around
+// all the includes, because the i386 sys/disklabel.h contains code which
+// c++ considers invalid.
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/cdio.h>
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+// ntohl(x) et al have been moved out of sys/param.h in FreeBSD 5
+#include <netinet/in.h>
+
+// XXX
+#define BX_CD_FRAMESIZE 2048
+#define CD_FRAMESIZE 2048
+
+#elif defined(__APPLE__)
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <dev/disk.h>
+#include <errno.h>
+#include <paths.h>
+#include <sys/param.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/storage/IOCDMedia.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDTypes.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+// These definitions were taken from mount_cd9660.c
+// There are some similar definitions in IOCDTypes.h
+// however there seems to be some dissagreement in
+// the definition of CDTOC.length
+struct _CDMSF {
+ u_char minute;
+ u_char second;
+ u_char frame;
+};
+
+#define MSF_TO_LBA(msf) \
+ (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
+
+struct _CDTOC_Desc {
+ u_char session;
+ u_char ctrl_adr; /* typed to be machine and compiler independent */
+ u_char tno;
+ u_char point;
+ struct _CDMSF address;
+ u_char zero;
+ struct _CDMSF p;
+};
+
+struct _CDTOC {
+ u_short length; /* in native cpu endian */
+ u_char first_session;
+ u_char last_session;
+ struct _CDTOC_Desc trackdesc[1];
+};
+
+static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator, mach_port_t *masterPort );
+static kern_return_t GetDeviceFilePath( io_iterator_t mediaIterator, char *deviceFilePath, CFIndex maxPathSize );
+//int OpenDrive( const char *deviceFilePath );
+static struct _CDTOC * ReadTOC( const char * devpath );
+
+static char CDDevicePath[ MAXPATHLEN ];
+
+#define BX_CD_FRAMESIZE 2048
+#define CD_FRAMESIZE 2048
+
+#elif defined(WIN32)
+// windows.h included by bochs.h
+#include <winioctl.h>
+#include "aspi-win32.h"
+#include "scsidefs.h"
+
+DWORD (*GetASPI32SupportInfo)(void);
+DWORD (*SendASPI32Command)(LPSRB);
+BOOL (*GetASPI32Buffer)(PASPI32BUFF);
+BOOL (*FreeASPI32Buffer)(PASPI32BUFF);
+BOOL (*TranslateASPI32Address)(PDWORD,PDWORD);
+DWORD (*GetASPI32DLLVersion)(void);
+
+
+static BOOL bUseASPI = FALSE;
+static BOOL bHaveDev;
+static UINT cdromCount = 0;
+static HINSTANCE hASPI = NULL;
+
+#define BX_CD_FRAMESIZE 2048
+#define CD_FRAMESIZE 2048
+
+#else // all others (Irix, Tru64)
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#define BX_CD_FRAMESIZE 2048
+#define CD_FRAMESIZE 2048
+#endif
+
+#include <stdio.h>
+
+#ifdef __APPLE__
+static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator,
+ mach_port_t *masterPort )
+{
+ kern_return_t kernResult;
+ CFMutableDictionaryRef classesToMatch;
+ kernResult = IOMasterPort( bootstrap_port, masterPort );
+ if ( kernResult != KERN_SUCCESS )
+ {
+ fprintf ( stderr, "IOMasterPort returned %d\n", kernResult );
+ return kernResult;
+ }
+ // CD media are instances of class kIOCDMediaClass.
+ classesToMatch = IOServiceMatching( kIOCDMediaClass );
+ if ( classesToMatch == NULL )
+ fprintf ( stderr, "IOServiceMatching returned a NULL dictionary.\n" );
+ else
+ {
+ // Each IOMedia object has a property with key kIOMediaEjectableKey
+ // which is true if the media is indeed ejectable. So add property
+ // to CFDictionary for matching.
+ CFDictionarySetValue( classesToMatch,
+ CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
+ }
+ kernResult = IOServiceGetMatchingServices( *masterPort,
+ classesToMatch, mediaIterator );
+ if ( (kernResult != KERN_SUCCESS) || (*mediaIterator == NULL) )
+ fprintf( stderr, "No ejectable CD media found.\n kernResult = %d\n", kernResult );
+
+ return kernResult;
+}
+
+
+static kern_return_t GetDeviceFilePath( io_iterator_t mediaIterator,
+ char *deviceFilePath, CFIndex maxPathSize )
+{
+ io_object_t nextMedia;
+ kern_return_t kernResult = KERN_FAILURE;
+ nextMedia = IOIteratorNext( mediaIterator );
+ if ( nextMedia == NULL )
+ {
+ *deviceFilePath = '\0';
+ }
+ else
+ {
+ CFTypeRef deviceFilePathAsCFString;
+ deviceFilePathAsCFString = IORegistryEntryCreateCFProperty(
+ nextMedia, CFSTR( kIOBSDNameKey ),
+ kCFAllocatorDefault, 0 );
+ *deviceFilePath = '\0';
+ if ( deviceFilePathAsCFString )
+ {
+ size_t devPathLength = strlen( _PATH_DEV );
+ strcpy( deviceFilePath, _PATH_DEV );
+ if ( CFStringGetCString( (const __CFString *) deviceFilePathAsCFString,
+ deviceFilePath + devPathLength,
+ maxPathSize - devPathLength,
+ kCFStringEncodingASCII ) )
+ {
+ // fprintf( stderr, "BSD path: %s\n", deviceFilePath );
+ kernResult = KERN_SUCCESS;
+ }
+ CFRelease( deviceFilePathAsCFString );
+ }
+ }
+ IOObjectRelease( nextMedia );
+ return kernResult;
+}
+
+
+static int OpenDrive( const char *deviceFilePath )
+{
+
+ int fileDescriptor;
+
+ fileDescriptor = open( deviceFilePath, O_RDONLY );
+ if ( fileDescriptor == -1 )
+ fprintf( stderr, "Error %d opening device %s.\n", errno, deviceFilePath );
+ return fileDescriptor;
+
+}
+
+static struct _CDTOC * ReadTOC( const char * devpath ) {
+
+ struct _CDTOC * toc_p = NULL;
+ io_iterator_t iterator = 0;
+ io_registry_entry_t service = 0;
+ CFDictionaryRef properties = 0;
+ CFDataRef data = 0;
+ mach_port_t port = 0;
+ char * devname;
+
+ if (( devname = strrchr( devpath, '/' )) != NULL ) {
+ ++devname;
+ }
+ else {
+ devname = (char *) devpath;
+ }
+
+ if ( IOMasterPort(bootstrap_port, &port ) != KERN_SUCCESS ) {
+ fprintf( stderr, "IOMasterPort failed\n" );
+ goto Exit;
+ }
+
+ if ( IOServiceGetMatchingServices( port, IOBSDNameMatching( port, 0, devname ),
+ &iterator ) != KERN_SUCCESS ) {
+ fprintf( stderr, "IOServiceGetMatchingServices failed\n" );
+ goto Exit;
+ }
+
+ service = IOIteratorNext( iterator );
+
+ IOObjectRelease( iterator );
+
+ iterator = 0;
+
+ while ( service && !IOObjectConformsTo( service, "IOCDMedia" )) {
+ if ( IORegistryEntryGetParentIterator( service, kIOServicePlane,
+ &iterator ) != KERN_SUCCESS ) {
+ fprintf( stderr, "IORegistryEntryGetParentIterator failed\n" );
+ goto Exit;
+ }
+
+ IOObjectRelease( service );
+ service = IOIteratorNext( iterator );
+ IOObjectRelease( iterator );
+
+ }
+
+ if ( service == NULL ) {
+ fprintf( stderr, "CD media not found\n" );
+ goto Exit;
+ }
+
+ if ( IORegistryEntryCreateCFProperties( service, (__CFDictionary **) &properties,
+ kCFAllocatorDefault,
+ kNilOptions ) != KERN_SUCCESS ) {
+ fprintf( stderr, "IORegistryEntryGetParentIterator failed\n" );
+ goto Exit;
+ }
+
+ data = (CFDataRef) CFDictionaryGetValue( properties, CFSTR(kIOCDMediaTOCKey) );
+ if ( data == NULL ) {
+ fprintf( stderr, "CFDictionaryGetValue failed\n" );
+ goto Exit;
+ }
+ else {
+
+ CFRange range;
+ CFIndex buflen;
+
+ buflen = CFDataGetLength( data ) + 1;
+ range = CFRangeMake( 0, buflen );
+ toc_p = (struct _CDTOC *) malloc( buflen );
+ if ( toc_p == NULL ) {
+ fprintf( stderr, "Out of memory\n" );
+ goto Exit;
+ }
+ else {
+ CFDataGetBytes( data, range, (unsigned char *) toc_p );
+ }
+
+ /*
+ fprintf( stderr, "Table of contents\n length %d first %d last %d\n",
+ toc_p->length, toc_p->first_session, toc_p->last_session );
+ */
+
+ CFRelease( properties );
+
+ }
+
+
+ Exit:
+
+ if ( service ) {
+ IOObjectRelease( service );
+ }
+
+ return toc_p;
+
+}
+#endif
+
+#ifdef WIN32
+
+bool ReadCDSector(unsigned int hid, unsigned int tid, unsigned int lun, unsigned long frame, unsigned char *buf, int bufsize)
+{
+ HANDLE hEventSRB;
+ SRB_ExecSCSICmd srb;
+ DWORD dwStatus;
+
+ hEventSRB = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ memset(&srb,0,sizeof(SRB_ExecSCSICmd));
+ srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ srb.SRB_HaId = hid;
+ srb.SRB_Target = tid;
+ srb.SRB_Lun = lun;
+ srb.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ srb.SRB_SenseLen = SENSE_LEN;
+ srb.SRB_PostProc = hEventSRB;
+ srb.SRB_BufPointer = buf;
+ srb.SRB_BufLen = bufsize;
+ srb.SRB_CDBLen = 10;
+ srb.CDBByte[0] = SCSI_READ10;
+ srb.CDBByte[2] = (unsigned char) (frame>>24);
+ srb.CDBByte[3] = (unsigned char) (frame>>16);
+ srb.CDBByte[4] = (unsigned char) (frame>>8);
+ srb.CDBByte[5] = (unsigned char) (frame);
+ srb.CDBByte[7] = 0;
+ srb.CDBByte[8] = 1; /* read 1 frames */
+
+ ResetEvent(hEventSRB);
+ dwStatus = SendASPI32Command((SRB *)&srb);
+ if(dwStatus == SS_PENDING) {
+ WaitForSingleObject(hEventSRB, 100000);
+ }
+ CloseHandle(hEventSRB);
+ return (srb.SRB_TargStat == STATUS_GOOD);
+}
+
+int GetCDCapacity(unsigned int hid, unsigned int tid, unsigned int lun)
+{
+ HANDLE hEventSRB;
+ SRB_ExecSCSICmd srb;
+ DWORD dwStatus;
+ unsigned char buf[8];
+
+ hEventSRB = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ memset(&buf, 0, sizeof(buf));
+ memset(&srb,0,sizeof(SRB_ExecSCSICmd));
+ srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
+ srb.SRB_HaId = hid;
+ srb.SRB_Target = tid;
+ srb.SRB_Lun = lun;
+ srb.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
+ srb.SRB_SenseLen = SENSE_LEN;
+ srb.SRB_PostProc = hEventSRB;
+ srb.SRB_BufPointer = (unsigned char *)buf;
+ srb.SRB_BufLen = 8;
+ srb.SRB_CDBLen = 10;
+ srb.CDBByte[0] = SCSI_READCDCAP;
+ srb.CDBByte[2] = 0;
+ srb.CDBByte[3] = 0;
+ srb.CDBByte[4] = 0;
+ srb.CDBByte[5] = 0;
+ srb.CDBByte[8] = 0;
+
+ ResetEvent(hEventSRB);
+ dwStatus = SendASPI32Command((SRB *)&srb);
+ if(dwStatus == SS_PENDING) {
+ WaitForSingleObject(hEventSRB, 100000);
+ }
+
+ CloseHandle(hEventSRB);
+ return ((buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]) * ((buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7]);
+}
+
+#endif
+
+cdrom_interface::cdrom_interface(char *dev)
+{
+ put("CD");
+ settype(CDLOG);
+ fd = -1; // File descriptor not yet allocated
+
+ if ( dev == NULL )
+ path = NULL;
+ else {
+ path = strdup(dev);
+ }
+ using_file=0;
+}
+
+void
+cdrom_interface::init(void) {
+ BX_DEBUG(("Init $Id: cdrom.cc,v 1.66 2003/12/08 23:49:48 danielg4 Exp $"));
+ BX_INFO(("file = '%s'",path));
+}
+
+cdrom_interface::~cdrom_interface(void)
+{
+#ifdef WIN32
+#else
+ if (fd >= 0)
+ close(fd);
+#endif
+ if (path)
+ free(path);
+ BX_DEBUG(("Exit"));
+}
+
+ bx_bool
+cdrom_interface::insert_cdrom(char *dev)
+{
+ unsigned char buffer[BX_CD_FRAMESIZE];
+ ssize_t ret;
+
+ // Load CD-ROM. Returns false if CD is not ready.
+ if (dev != NULL) path = strdup(dev);
+ BX_INFO (("load cdrom with path=%s", path));
+#ifdef WIN32
+ char drive[256];
+ OSVERSIONINFO osi;
+ if ( (path[1] == ':') && (strlen(path) == 2) )
+ {
+ osi.dwOSVersionInfoSize = sizeof(osi);
+ GetVersionEx(&osi);
+ if(osi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ // Use direct device access under windows NT/2k
+
+ // With all the backslashes it's hard to see, but to open D: drive
+ // the name would be: \\.\d:
+ sprintf(drive, "\\\\.\\%s", path);
+ using_file = 0;
+ BX_INFO (("Using direct access for cdrom."));
+ // This trick only works for Win2k and WinNT, so warn the user of that.
+ } else {
+ BX_INFO(("Using ASPI for cdrom. Drive letters are unused yet."));
+ bUseASPI = TRUE;
+ }
+ }
+ else
+ {
+ strcpy(drive,path);
+ using_file = 1;
+ bUseASPI = FALSE;
+ BX_INFO (("Opening image file as a cd"));
+ }
+ if(bUseASPI) {
+ DWORD d;
+ UINT cdr, cnt, max;
+ UINT i, j, k;
+ SRB_HAInquiry sh;
+ SRB_GDEVBlock sd;
+ if (!hASPI) {
+ hASPI = LoadLibrary("WNASPI32.DLL");
+ }
+ if(hASPI) {
+ SendASPI32Command = (DWORD(*)(LPSRB))GetProcAddress( hASPI, "SendASPI32Command" );
+ GetASPI32DLLVersion = (DWORD(*)(void))GetProcAddress( hASPI, "GetASPI32DLLVersion" );
+ GetASPI32SupportInfo = (DWORD(*)(void))GetProcAddress( hASPI, "GetASPI32SupportInfo" );
+// BX_INFO(("Using first CDROM. Please upgrade your ASPI drivers to version 4.01 or later if you wish to specify a cdrom driver."));
+
+ cdr = 0;
+ bHaveDev = FALSE;
+ d = GetASPI32SupportInfo();
+ cnt = LOBYTE(LOWORD(d));
+ for(i = 0; i < cnt; i++) {
+ memset(&sh, 0, sizeof(sh));
+ sh.SRB_Cmd = SC_HA_INQUIRY;
+ sh.SRB_HaId = i;
+ SendASPI32Command((LPSRB)&sh);
+ if(sh.SRB_Status != SS_COMP)
+ continue;
+
+ max = (int)sh.HA_Unique[3];
+ for(j = 0; j < max; j++) {
+ for(k = 0; k < 8; k++) {
+ memset(&sd, 0, sizeof(sd));
+ sd.SRB_Cmd = SC_GET_DEV_TYPE;
+ sd.SRB_HaId = i;
+ sd.SRB_Target = j;
+ sd.SRB_Lun = k;
+ SendASPI32Command((LPSRB)&sd);
+ if(sd.SRB_Status == SS_COMP) {
+ if(sd.SRB_DeviceType == DTYPE_CDROM) {
+ cdr++;
+ if(cdr > cdromCount) {
+ hid = i;
+ tid = j;
+ lun = k;
+ cdromCount++;
+ bHaveDev = TRUE;
+ }
+ }
+ }
+ if(bHaveDev) break;
+ }
+ if(bHaveDev) break;
+ }
+
+ }
+ } else {
+ BX_PANIC(("Could not load ASPI drivers, so cdrom access will fail"));
+ }
+ fd=1;
+ } else {
+ BX_INFO(("Using direct access for CDROM"));
+ hFile=CreateFile((char *)&drive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
+ if (hFile !=(void *)0xFFFFFFFF)
+ fd=1;
+ }
+#elif defined(__APPLE__)
+ if(strcmp(path, "drive") == 0)
+ {
+ mach_port_t masterPort = NULL;
+ io_iterator_t mediaIterator;
+ kern_return_t kernResult;
+
+ BX_INFO(( "Insert CDROM" ));
+
+ kernResult = FindEjectableCDMedia( &mediaIterator, &masterPort );
+ if ( kernResult != KERN_SUCCESS ) {
+ BX_INFO (("Unable to find CDROM"));
+ return false;
+ }
+
+ kernResult = GetDeviceFilePath( mediaIterator, CDDevicePath, sizeof( CDDevicePath ) );
+ if ( kernResult != KERN_SUCCESS ) {
+ BX_INFO (("Unable to get CDROM device file path" ));
+ return false;
+ }
+
+ // Here a cdrom was found so see if we can read from it.
+ // At this point a failure will result in panic.
+ if ( strlen( CDDevicePath ) ) {
+ fd = open(CDDevicePath, O_RDONLY);
+ }
+ }
+ else
+ {
+ fd = open(path, O_RDONLY);
+ }
+#else
+ // all platforms except win32
+ fd = open(path, O_RDONLY);
+#endif
+ if (fd < 0) {
+ BX_ERROR(( "open cd failed for %s: %s", path, strerror(errno)));
+ return(false);
+ }
+
+ // I just see if I can read a sector to verify that a
+ // CD is in the drive and readable.
+#ifdef WIN32
+ if(bUseASPI) {
+ return ReadCDSector(hid, tid, lun, 0, buffer, BX_CD_FRAMESIZE);
+ } else {
+ if (!ReadFile(hFile, (void *) buffer, BX_CD_FRAMESIZE, (unsigned long *) &ret, NULL)) {
+ CloseHandle(hFile);
+ fd = -1;
+ BX_DEBUG(( "insert_cdrom: read returns error." ));
+ return(false);
+ }
+ }
+#else
+ // do fstat to determine if it's a file or a device, then set using_file.
+ struct stat stat_buf;
+ ret = fstat (fd, &stat_buf);
+ if (ret) {
+ BX_PANIC (("fstat cdrom file returned error: %s", strerror (errno)));
+ }
+ if (S_ISREG (stat_buf.st_mode)) {
+ using_file = 1;
+ BX_INFO (("Opening image file %s as a cd.", path));
+ } else {
+ using_file = 0;
+ BX_INFO (("Using direct access for cdrom."));
+ }
+
+ ret = read(fd, (char*) &buffer, BX_CD_FRAMESIZE);
+ if (ret < 0) {
+ close(fd);
+ fd = -1;
+ BX_DEBUG(( "insert_cdrom: read returns error: %s", strerror (errno) ));
+ return(false);
+ }
+#endif
+ return(true);
+}
+
+ int
+cdrom_interface::start_cdrom()
+{
+ // Spin up the cdrom drive.
+
+ if (fd >= 0) {
+#if defined(__NetBSD__)
+ if (ioctl (fd, CDIOCSTART) < 0)
+ BX_DEBUG(( "start_cdrom: start returns error: %s", strerror (errno) ));
+ return(true);
+#else
+ BX_INFO(("start_cdrom: your OS is not supported yet."));
+ return(false); // OS not supported yet, return false always.
+#endif
+ }
+ return(false);
+}
+
+ void
+cdrom_interface::eject_cdrom()
+{
+ // Logically eject the CD. I suppose we could stick in
+ // some ioctl() calls to really eject the CD as well.
+
+ if (fd >= 0) {
+#if (defined(__OpenBSD__) || defined(__FreeBSD__))
+ (void) ioctl (fd, CDIOCALLOW);
+ if (ioctl (fd, CDIOCEJECT) < 0)
+ BX_DEBUG(( "eject_cdrom: eject returns error." ));
+#endif
+
+#ifdef WIN32
+if (using_file == 0)
+{
+ if(bUseASPI) {
+ } else {
+ DWORD lpBytesReturned;
+ DeviceIoControl(hFile, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &lpBytesReturned, NULL);
+ }
+}
+#else // WIN32
+
+#if __linux__
+ if (!using_file)
+ ioctl (fd, CDROMEJECT, NULL);
+#endif
+
+ close(fd);
+#endif // WIN32
+ fd = -1;
+ }
+}
+
+
+ bx_bool
+cdrom_interface::read_toc(uint8* buf, int* length, bx_bool msf, int start_track)
+{
+ // Read CD TOC. Returns false if start track is out of bounds.
+
+ if (fd < 0) {
+ BX_PANIC(("cdrom: read_toc: file not open."));
+ }
+
+#if defined(WIN32)
+ if (1) { // This is a hack and works okay if there's one rom track only
+#else
+ if (using_file) {
+#endif
+ // From atapi specs : start track can be 0-63, AA
+ if ((start_track > 1) && (start_track != 0xaa))
+ return false;
+
+ buf[2] = 1;
+ buf[3] = 1;
+
+ int len = 4;
+ if (start_track <= 1) {
+ buf[len++] = 0; // Reserved
+ buf[len++] = 0x14; // ADR, control
+ buf[len++] = 1; // Track number
+ buf[len++] = 0; // Reserved
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = 0; // minute
+ buf[len++] = 2; // second
+ buf[len++] = 0; // frame
+ } else {
+ buf[len++] = 0;
+ buf[len++] = 0;
+ buf[len++] = 0;
+ buf[len++] = 0; // logical sector 0
+ }
+ }
+
+ // Lead out track
+ buf[len++] = 0; // Reserved
+ buf[len++] = 0x16; // ADR, control
+ buf[len++] = 0xaa; // Track number
+ buf[len++] = 0; // Reserved
+
+ uint32 blocks = capacity();
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = (uint8)(((blocks + 150) / 75) / 60); // minute
+ buf[len++] = (uint8)(((blocks + 150) / 75) % 60); // second
+ buf[len++] = (uint8)((blocks + 150) % 75); // frame;
+ } else {
+ buf[len++] = (blocks >> 24) & 0xff;
+ buf[len++] = (blocks >> 16) & 0xff;
+ buf[len++] = (blocks >> 8) & 0xff;
+ buf[len++] = (blocks >> 0) & 0xff;
+ }
+
+ buf[0] = ((len-2) >> 8) & 0xff;
+ buf[1] = (len-2) & 0xff;
+
+ *length = len;
+
+ return true;
+ }
+ // all these implementations below are the platform-dependent code required
+ // to read the TOC from a physical cdrom.
+#ifdef WIN32
+ {
+/* #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
+ #define IOCTL_CDROM_READ_TOC CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
+ unsigned long iBytesReturned;
+ DeviceIoControl(hFile, IOCTL_CDROM_READ_TOC, NULL, 0, NULL, 0, &iBytesReturned, NULL); */
+ BX_ERROR (("WARNING: read_toc is not implemented, just returning length=1"));
+ *length = 1;
+ return true;
+ }
+#elif __linux__ || defined(__sun)
+ {
+ struct cdrom_tochdr tochdr;
+ if (ioctl(fd, CDROMREADTOCHDR, &tochdr))
+ BX_PANIC(("cdrom: read_toc: READTOCHDR failed."));
+
+ if ((start_track > tochdr.cdth_trk1) && (start_track != 0xaa))
+ return false;
+
+ buf[2] = tochdr.cdth_trk0;
+ buf[3] = tochdr.cdth_trk1;
+
+ if (start_track < tochdr.cdth_trk0)
+ start_track = tochdr.cdth_trk0;
+
+ int len = 4;
+ for (int i = start_track; i <= tochdr.cdth_trk1; i++) {
+ struct cdrom_tocentry tocentry;
+ tocentry.cdte_format = (msf) ? CDROM_MSF : CDROM_LBA;
+ tocentry.cdte_track = i;
+ if (ioctl(fd, CDROMREADTOCENTRY, &tocentry))
+ BX_PANIC(("cdrom: read_toc: READTOCENTRY failed."));
+ buf[len++] = 0; // Reserved
+ buf[len++] = (tocentry.cdte_adr << 4) | tocentry.cdte_ctrl ; // ADR, control
+ buf[len++] = i; // Track number
+ buf[len++] = 0; // Reserved
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = tocentry.cdte_addr.msf.minute;
+ buf[len++] = tocentry.cdte_addr.msf.second;
+ buf[len++] = tocentry.cdte_addr.msf.frame;
+ } else {
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 24) & 0xff;
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 16) & 0xff;
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 8) & 0xff;
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 0) & 0xff;
+ }
+ }
+
+ // Lead out track
+ struct cdrom_tocentry tocentry;
+ tocentry.cdte_format = (msf) ? CDROM_MSF : CDROM_LBA;
+#ifdef CDROM_LEADOUT
+ tocentry.cdte_track = CDROM_LEADOUT;
+#else
+ tocentry.cdte_track = 0xaa;
+#endif
+ if (ioctl(fd, CDROMREADTOCENTRY, &tocentry))
+ BX_PANIC(("cdrom: read_toc: READTOCENTRY lead-out failed."));
+ buf[len++] = 0; // Reserved
+ buf[len++] = (tocentry.cdte_adr << 4) | tocentry.cdte_ctrl ; // ADR, control
+ buf[len++] = 0xaa; // Track number
+ buf[len++] = 0; // Reserved
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = tocentry.cdte_addr.msf.minute;
+ buf[len++] = tocentry.cdte_addr.msf.second;
+ buf[len++] = tocentry.cdte_addr.msf.frame;
+ } else {
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 24) & 0xff;
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 16) & 0xff;
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 8) & 0xff;
+ buf[len++] = (((unsigned)tocentry.cdte_addr.lba) >> 0) & 0xff;
+ }
+
+ buf[0] = ((len-2) >> 8) & 0xff;
+ buf[1] = (len-2) & 0xff;
+
+ *length = len;
+
+ return true;
+ }
+#elif (defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__))
+ {
+ struct ioc_toc_header h;
+ struct ioc_read_toc_entry t;
+
+ if (ioctl (fd, CDIOREADTOCHEADER, &h) < 0)
+ BX_PANIC(("cdrom: read_toc: READTOCHDR failed."));
+
+ if ((start_track > h.ending_track) && (start_track != 0xaa))
+ return false;
+
+ buf[2] = h.starting_track;
+ buf[3] = h.ending_track;
+
+ if (start_track < h.starting_track)
+ start_track = h.starting_track;
+
+ int len = 4;
+ for (int i = start_track; i <= h.ending_track; i++) {
+ struct cd_toc_entry tocentry;
+ t.address_format = (msf) ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ t.starting_track = i;
+ t.data_len = sizeof(tocentry);
+ t.data = &tocentry;
+
+ if (ioctl (fd, CDIOREADTOCENTRYS, &t) < 0)
+ BX_PANIC(("cdrom: read_toc: READTOCENTRY failed."));
+
+ buf[len++] = 0; // Reserved
+ buf[len++] = (tocentry.addr_type << 4) | tocentry.control ; // ADR, control
+ buf[len++] = i; // Track number
+ buf[len++] = 0; // Reserved
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = tocentry.addr.msf.minute;
+ buf[len++] = tocentry.addr.msf.second;
+ buf[len++] = tocentry.addr.msf.frame;
+ } else {
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 24) & 0xff;
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 16) & 0xff;
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 8) & 0xff;
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 0) & 0xff;
+ }
+ }
+
+ // Lead out track
+ struct cd_toc_entry tocentry;
+ t.address_format = (msf) ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ t.starting_track = 0xaa;
+ t.data_len = sizeof(tocentry);
+ t.data = &tocentry;
+
+ if (ioctl (fd, CDIOREADTOCENTRYS, &t) < 0)
+ BX_PANIC(("cdrom: read_toc: READTOCENTRY lead-out failed."));
+
+ buf[len++] = 0; // Reserved
+ buf[len++] = (tocentry.addr_type << 4) | tocentry.control ; // ADR, control
+ buf[len++] = 0xaa; // Track number
+ buf[len++] = 0; // Reserved
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = tocentry.addr.msf.minute;
+ buf[len++] = tocentry.addr.msf.second;
+ buf[len++] = tocentry.addr.msf.frame;
+ } else {
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 24) & 0xff;
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 16) & 0xff;
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 8) & 0xff;
+ buf[len++] = (((unsigned)tocentry.addr.lba) >> 0) & 0xff;
+ }
+
+ buf[0] = ((len-2) >> 8) & 0xff;
+ buf[1] = (len-2) & 0xff;
+
+ *length = len;
+
+ return true;
+ }
+#elif defined(__APPLE__)
+ // Read CD TOC. Returns false if start track is out of bounds.
+
+#if 1
+ {
+ struct _CDTOC * toc = ReadTOC( CDDevicePath );
+
+ if ((start_track > toc->last_session) && (start_track != 0xaa))
+ return false;
+
+ buf[2] = toc->first_session;
+ buf[3] = toc->last_session;
+
+ if (start_track < toc->first_session)
+ start_track = toc->first_session;
+
+ int len = 4;
+ for (int i = start_track; i <= toc->last_session; i++) {
+ buf[len++] = 0; // Reserved
+ buf[len++] = toc->trackdesc[i].ctrl_adr ; // ADR, control
+ buf[len++] = i; // Track number
+ buf[len++] = 0; // Reserved
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = toc->trackdesc[i].address.minute;
+ buf[len++] = toc->trackdesc[i].address.second;
+ buf[len++] = toc->trackdesc[i].address.frame;
+ } else {
+ unsigned lba = (unsigned)(MSF_TO_LBA(toc->trackdesc[i].address));
+ buf[len++] = (lba >> 24) & 0xff;
+ buf[len++] = (lba >> 16) & 0xff;
+ buf[len++] = (lba >> 8) & 0xff;
+ buf[len++] = (lba >> 0) & 0xff;
+ }
+ }
+
+ // Lead out track
+ buf[len++] = 0; // Reserved
+ buf[len++] = 0x16; // ADR, control
+ buf[len++] = 0xaa; // Track number
+ buf[len++] = 0; // Reserved
+
+ uint32 blocks = capacity();
+
+ // Start address
+ if (msf) {
+ buf[len++] = 0; // reserved
+ buf[len++] = (uint8)(((blocks + 150) / 75) / 60); // minute
+ buf[len++] = (uint8)(((blocks + 150) / 75) % 60); // second
+ buf[len++] = (uint8)((blocks + 150) % 75); // frame;
+ } else {
+ buf[len++] = (blocks >> 24) & 0xff;
+ buf[len++] = (blocks >> 16) & 0xff;
+ buf[len++] = (blocks >> 8) & 0xff;
+ buf[len++] = (blocks >> 0) & 0xff;
+ }
+
+ buf[0] = ((len-2) >> 8) & 0xff;
+ buf[1] = (len-2) & 0xff;
+
+ *length = len;
+
+ return true;
+// BX_INFO(( "Read TOC - Not Implemented" ));
+// return false;
+ }
+#else
+ BX_INFO(( "Read TOC - Not Implemented" ));
+ return false;
+#endif
+#else
+ BX_INFO(("read_toc: your OS is not supported yet."));
+ return(false); // OS not supported yet, return false always.
+#endif
+}
+
+
+ uint32
+cdrom_interface::capacity()
+{
+ // Return CD-ROM capacity. I believe you want to return
+ // the number of blocks of capacity the actual media has.
+
+#if !defined WIN32
+ // win32 has its own way of doing this
+ if (using_file) {
+ // return length of the image file
+ struct stat stat_buf;
+ int ret = fstat (fd, &stat_buf);
+ if (ret) {
+ BX_PANIC (("fstat on cdrom image returned err: %s", strerror(errno)));
+ }
+ BX_INFO (("cdrom size is %lld bytes", stat_buf.st_size));
+ if ((stat_buf.st_size % 2048) != 0) {
+ BX_ERROR (("expected cdrom image to be a multiple of 2048 bytes"));
+ }
+ return stat_buf.st_size / 2048;
+ }
+#endif
+
+#ifdef __BEOS__
+ return GetNumDeviceBlocks(fd, BX_CD_FRAMESIZE);
+#elif defined(__sun)
+ {
+ struct stat buf = {0};
+
+ if (fd < 0) {
+ BX_PANIC(("cdrom: capacity: file not open."));
+ }
+
+ if( fstat(fd, &buf) != 0 )
+ BX_PANIC(("cdrom: capacity: stat() failed."));
+
+ return(buf.st_size);
+ }
+#elif (defined(__NetBSD__) || defined(__OpenBSD__))
+ {
+ // We just read the disklabel, imagine that...
+ struct disklabel lp;
+
+ if (fd < 0)
+ BX_PANIC(("cdrom: capacity: file not open."));
+
+ if (ioctl(fd, DIOCGDINFO, &lp) < 0)
+ BX_PANIC(("cdrom: ioctl(DIOCGDINFO) failed"));
+
+ BX_DEBUG(( "capacity: %u", lp.d_secperunit ));
+ return(lp.d_secperunit);
+ }
+#elif defined(__linux__)
+ {
+ // Read the TOC to get the data size, since BLKGETSIZE doesn't work on
+ // non-ATAPI drives. This is based on Keith Jones code below.
+ // <splite@purdue.edu> 21 June 2001
+
+ int i, dtrk_lba, num_sectors;
+ int dtrk = 0;
+ struct cdrom_tochdr td;
+ struct cdrom_tocentry te;
+
+ if (fd < 0)
+ BX_PANIC(("cdrom: capacity: file not open."));
+
+ if (ioctl(fd, CDROMREADTOCHDR, &td) < 0)
+ BX_PANIC(("cdrom: ioctl(CDROMREADTOCHDR) failed"));
+
+ num_sectors = -1;
+ dtrk_lba = -1;
+
+ for (i = td.cdth_trk0; i <= td.cdth_trk1; i++) {
+ te.cdte_track = i;
+ te.cdte_format = CDROM_LBA;
+ if (ioctl(fd, CDROMREADTOCENTRY, &te) < 0)
+ BX_PANIC(("cdrom: ioctl(CDROMREADTOCENTRY) failed"));
+
+ if (dtrk_lba != -1) {
+ num_sectors = te.cdte_addr.lba - dtrk_lba;
+ break;
+ }
+ if (te.cdte_ctrl & CDROM_DATA_TRACK) {
+ dtrk = i;
+ dtrk_lba = te.cdte_addr.lba;
+ }
+ }
+
+ if (num_sectors < 0) {
+ if (dtrk_lba != -1) {
+ te.cdte_track = CDROM_LEADOUT;
+ te.cdte_format = CDROM_LBA;
+ if (ioctl(fd, CDROMREADTOCENTRY, &te) < 0)
+ BX_PANIC(("cdrom: ioctl(CDROMREADTOCENTRY) failed"));
+ num_sectors = te.cdte_addr.lba - dtrk_lba;
+ } else
+ BX_PANIC(("cdrom: no data track found"));
+ }
+
+ BX_INFO(("cdrom: Data track %d, length %d", dtrk, num_sectors));
+
+ return(num_sectors);
+
+ }
+#elif defined(__FreeBSD__)
+ {
+ // Read the TOC to get the size of the data track.
+ // Keith Jones <freebsd.dev@blueyonder.co.uk>, 16 January 2000
+
+#define MAX_TRACKS 100
+
+ int i, num_tracks, num_sectors;
+ struct ioc_toc_header td;
+ struct ioc_read_toc_entry rte;
+ struct cd_toc_entry toc_buffer[MAX_TRACKS + 1];
+
+ if (fd < 0)
+ BX_PANIC(("cdrom: capacity: file not open."));
+
+ if (ioctl(fd, CDIOREADTOCHEADER, &td) < 0)
+ BX_PANIC(("cdrom: ioctl(CDIOREADTOCHEADER) failed"));
+
+ num_tracks = (td.ending_track - td.starting_track) + 1;
+ if (num_tracks > MAX_TRACKS)
+ BX_PANIC(("cdrom: TOC is too large"));
+
+ rte.address_format = CD_LBA_FORMAT;
+ rte.starting_track = td.starting_track;
+ rte.data_len = (num_tracks + 1) * sizeof(struct cd_toc_entry);
+ rte.data = toc_buffer;
+ if (ioctl(fd, CDIOREADTOCENTRYS, &rte) < 0)
+ BX_PANIC(("cdrom: ioctl(CDIOREADTOCENTRYS) failed"));
+
+ num_sectors = -1;
+ for (i = 0; i < num_tracks; i++) {
+ if (rte.data[i].control & 4) { /* data track */
+ num_sectors = ntohl(rte.data[i + 1].addr.lba)
+ - ntohl(rte.data[i].addr.lba);
+ BX_INFO(( "cdrom: Data track %d, length %d",
+ rte.data[i].track, num_sectors));
+ break;
+ }
+ }
+
+ if (num_sectors < 0)
+ BX_PANIC(("cdrom: no data track found"));
+
+ return(num_sectors);
+
+ }
+#elif defined WIN32
+ {
+ if(bUseASPI) {
+ return (GetCDCapacity(hid, tid, lun) / 2352);
+ } else if(using_file) {
+ ULARGE_INTEGER FileSize;
+ FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart);
+ return (FileSize.QuadPart / 2048);
+ } else { /* direct device access */
+ DWORD SectorsPerCluster;
+ DWORD TotalNumOfClusters;
+ GetDiskFreeSpace( path, &SectorsPerCluster, NULL, NULL, &TotalNumOfClusters);
+ return (TotalNumOfClusters * SectorsPerCluster);
+ }
+ }
+#elif defined __APPLE__
+// Find the size of the first data track on the cd. This has produced
+// the same results as the linux version on every cd I have tried, about
+// 5. The differences here seem to be that the entries in the TOC when
+// retrieved from the IOKit interface appear in a reversed order when
+// compared with the linux READTOCENTRY ioctl.
+ {
+ // Return CD-ROM capacity. I believe you want to return
+ // the number of bytes of capacity the actual media has.
+
+ BX_INFO(( "Capacity" ));
+
+ struct _CDTOC * toc = ReadTOC( CDDevicePath );
+
+ if ( toc == NULL ) {
+ BX_PANIC(( "capacity: Failed to read toc" ));
+ }
+
+ size_t toc_entries = ( toc->length - 2 ) / sizeof( struct _CDTOC_Desc );
+
+ BX_DEBUG(( "reading %d toc entries\n", toc_entries ));
+
+ int start_sector = -1;
+ int data_track = -1;
+
+ // Iterate through the list backward. Pick the first data track and
+ // get the address of the immediately previous (or following depending
+ // on how you look at it). The difference in the sector numbers
+ // is returned as the sized of the data track.
+ for ( int i=toc_entries - 1; i>=0; i-- ) {
+
+ BX_DEBUG(( "session %d ctl_adr %d tno %d point %d lba %d z %d p lba %d\n",
+ (int)toc->trackdesc[i].session,
+ (int)toc->trackdesc[i].ctrl_adr,
+ (int)toc->trackdesc[i].tno,
+ (int)toc->trackdesc[i].point,
+ MSF_TO_LBA( toc->trackdesc[i].address ),
+ (int)toc->trackdesc[i].zero,
+ MSF_TO_LBA(toc->trackdesc[i].p )));
+
+ if ( start_sector != -1 ) {
+
+ start_sector = MSF_TO_LBA(toc->trackdesc[i].p) - start_sector;
+ break;
+
+ }
+
+ if (( toc->trackdesc[i].ctrl_adr >> 4) != 1 ) continue;
+
+ if ( toc->trackdesc[i].ctrl_adr & 0x04 ) {
+
+ data_track = toc->trackdesc[i].point;
+
+ start_sector = MSF_TO_LBA(toc->trackdesc[i].p);
+
+ }
+
+ }
+
+ free( toc );
+
+ if ( start_sector == -1 ) {
+ start_sector = 0;
+ }
+
+ BX_INFO(("first data track %d data size is %d", data_track, start_sector));
+
+ return start_sector;
+ }
+#else
+ BX_ERROR(( "capacity: your OS is not supported yet." ));
+ return(0);
+#endif
+}
+
+ void BX_CPP_AttrRegparmN(2)
+cdrom_interface::read_block(uint8* buf, int lba)
+{
+ // Read a single block from the CD
+
+#ifdef WIN32
+ LARGE_INTEGER pos;
+#else
+ off_t pos;
+#endif
+ ssize_t n;
+
+#ifdef WIN32
+ if(bUseASPI) {
+ ReadCDSector(hid, tid, lun, lba, buf, BX_CD_FRAMESIZE);
+ n = BX_CD_FRAMESIZE;
+ } else {
+ pos.QuadPart = (LONGLONG)lba*BX_CD_FRAMESIZE;
+ pos.LowPart = SetFilePointer(hFile, pos.LowPart, &pos.HighPart, SEEK_SET);
+ if ((pos.LowPart == 0xffffffff) && (GetLastError() != NO_ERROR)) {
+ BX_PANIC(("cdrom: read_block: SetFilePointer returned error."));
+ }
+ ReadFile(hFile, (void *) buf, BX_CD_FRAMESIZE, (unsigned long *) &n, NULL);
+ }
+#elif defined(__APPLE__)
+#define CD_SEEK_DISTANCE kCDSectorSizeWhole
+ if(using_file)
+ {
+ pos = lseek(fd, lba*BX_CD_FRAMESIZE, SEEK_SET);
+ if (pos < 0) {
+ BX_PANIC(("cdrom: read_block: lseek returned error."));
+ }
+ n = read(fd, buf, BX_CD_FRAMESIZE);
+ }
+ else
+ {
+ // This seek will leave us 16 bytes from the start of the data
+ // hence the magic number.
+ pos = lseek(fd, lba*CD_SEEK_DISTANCE + 16, SEEK_SET);
+ if (pos < 0) {
+ BX_PANIC(("cdrom: read_block: lseek returned error."));
+ }
+ n = read(fd, buf, CD_FRAMESIZE);
+ }
+#else
+ pos = lseek(fd, lba*BX_CD_FRAMESIZE, SEEK_SET);
+ if (pos < 0) {
+ BX_PANIC(("cdrom: read_block: lseek returned error."));
+ }
+ n = read(fd, (char*) buf, BX_CD_FRAMESIZE);
+#endif
+
+ if (n != BX_CD_FRAMESIZE) {
+ BX_PANIC(("cdrom: read_block: read returned %d",
+ (int) n));
+ }
+}
+
+#endif /* if BX_SUPPORT_CDROM */
diff --git a/tools/ioemu/iodev/cdrom.h b/tools/ioemu/iodev/cdrom.h
new file mode 100644
index 0000000000..29fdfaff85
--- /dev/null
+++ b/tools/ioemu/iodev/cdrom.h
@@ -0,0 +1,67 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: cdrom.h,v 1.13 2003/08/19 00:37:03 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Header file for low-level OS specific CDROM emulation
+
+
+class cdrom_interface : public logfunctions {
+public:
+ cdrom_interface(char *dev);
+ ~cdrom_interface(void);
+ void init(void);
+
+ // Load CD-ROM. Returns false if CD is not ready.
+ bx_bool insert_cdrom(char *dev = NULL);
+
+ // Logically eject the CD.
+ void eject_cdrom();
+
+ // Read CD TOC. Returns false if start track is out of bounds.
+ bx_bool read_toc(uint8* buf, int* length, bx_bool msf, int start_track);
+
+ // Return CD-ROM capacity (in 2048 byte frames)
+ uint32 capacity();
+
+ // Read a single block from the CD
+ void read_block(uint8* buf, int lba) BX_CPP_AttrRegparmN(2);
+
+ // Start (spin up) the CD.
+ int start_cdrom();
+
+private:
+ int fd;
+ char *path;
+
+ int using_file;
+#ifdef WIN32
+ HANDLE hFile;
+ int hid;
+ int tid;
+ int lun;
+#endif
+ };
+
diff --git a/tools/ioemu/iodev/cdrom_beos.h b/tools/ioemu/iodev/cdrom_beos.h
new file mode 100644
index 0000000000..8a22d5c0f8
--- /dev/null
+++ b/tools/ioemu/iodev/cdrom_beos.h
@@ -0,0 +1,10 @@
+#ifndef CDROM_BEOS_H
+#define CDROM_BEOS_H
+
+#include <stddef.h> //for off_t
+
+off_t GetNumDeviceBlocks(int fd, int block_size);
+int GetLogicalBlockSize(int fd);
+int GetDeviceBlockSize(int fd);
+
+#endif
diff --git a/tools/ioemu/iodev/cmos.cc b/tools/ioemu/iodev/cmos.cc
new file mode 100644
index 0000000000..fbf3144989
--- /dev/null
+++ b/tools/ioemu/iodev/cmos.cc
@@ -0,0 +1,824 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: cmos.cc,v 1.44 2003/12/27 13:43:41 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS theCmosDevice->
+
+bx_cmos_c *theCmosDevice = NULL;
+
+// CMOS register definitions from Ralf Brown's interrupt list v6.1, in a file
+// called cmos.lst. In cases where there are multiple uses for a given
+// register in the interrupt list, I only listed the purpose that Bochs
+// actually uses it for, but I wrote "alternatives" next to it.
+#define REG_SEC 0x00
+#define REG_SEC_ALARM 0x01
+#define REG_MIN 0x02
+#define REG_MIN_ALARM 0x03
+#define REG_HOUR 0x04
+#define REG_HOUR_ALARM 0x05
+#define REG_WEEK_DAY 0x06
+#define REG_MONTH_DAY 0x07
+#define REG_MONTH 0x08
+#define REG_YEAR 0x09
+#define REG_STAT_A 0x0a
+#define REG_STAT_B 0x0b
+#define REG_STAT_C 0x0c
+#define REG_STAT_D 0x0d
+#define REG_DIAGNOSTIC_STATUS 0x0e /* alternatives */
+#define REG_SHUTDOWN_STATUS 0x0f
+#define REG_EQUIPMENT_BYTE 0x14
+#define REG_CSUM_HIGH 0x2e
+#define REG_CSUM_LOW 0x2f
+#define REG_IBM_CENTURY_BYTE 0x32 /* alternatives */
+#define REG_IBM_PS2_CENTURY_BYTE 0x37 /* alternatives */
+
+// Bochs CMOS map (to be completed)
+//
+// Idx Len Description
+// 0x15 2 Base memory in 1k
+// 0x17 2 Memory size above 1M in 1k
+// 0x30 2 Memory size above 1M in 1k
+// 0x34 2 Memory size above 16M in 64k
+//
+
+// check that BX_NUM_CMOS_REGS is 64 or 128
+#if (BX_NUM_CMOS_REGS == 64)
+#elif (BX_NUM_CMOS_REGS == 128)
+#else
+#error "Invalid BX_NUM_CMOS_REGS value in config.h"
+#endif
+
+
+ int
+libcmos_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theCmosDevice = new bx_cmos_c ();
+ bx_devices.pluginCmosDevice = theCmosDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theCmosDevice, BX_PLUGIN_CMOS);
+ return(0); // Success
+}
+
+ void
+libcmos_LTX_plugin_fini(void)
+{
+}
+
+bx_cmos_c::bx_cmos_c(void)
+{
+ put("CMOS");
+ settype(CMOSLOG);
+
+ unsigned i;
+ for (i=0; i<BX_NUM_CMOS_REGS; i++)
+ s.reg[i] = 0;
+ s.periodic_timer_index = BX_NULL_TIMER_HANDLE;
+ s.one_second_timer_index = BX_NULL_TIMER_HANDLE;
+ s.uip_timer_index = BX_NULL_TIMER_HANDLE;
+}
+
+bx_cmos_c::~bx_cmos_c(void)
+{
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_cmos_c::init(void)
+{
+ BX_DEBUG(("Init $Id: cmos.cc,v 1.44 2003/12/27 13:43:41 vruppert Exp $"));
+ // CMOS RAM & RTC
+
+ DEV_register_ioread_handler(this, read_handler, 0x0070, "CMOS RAM", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0071, "CMOS RAM", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0070, "CMOS RAM", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0071, "CMOS RAM", 1);
+ DEV_register_irq(8, "CMOS RTC");
+ if (BX_CMOS_THIS s.periodic_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_CMOS_THIS s.periodic_timer_index =
+ DEV_register_timer(this, periodic_timer_handler,
+ 1000000, 1,0, "cmos"); // continuous, not-active
+ }
+ if (BX_CMOS_THIS s.one_second_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_CMOS_THIS s.one_second_timer_index =
+ DEV_register_timer(this, one_second_timer_handler,
+ 1000000, 1,0, "cmos"); // continuous, not-active
+ }
+ if (BX_CMOS_THIS s.uip_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_CMOS_THIS s.uip_timer_index =
+ DEV_register_timer(this, uip_timer_handler,
+ 244, 0, 0, "cmos"); // one-shot, not-active
+ }
+
+#if BX_USE_SPECIFIED_TIME0 != 0
+ // ??? this will not be correct for using an image file.
+ // perhaps take values in CMOS and work backwards to find
+ // s.timeval from values read in.
+ BX_CMOS_THIS s.timeval = BX_USE_SPECIFIED_TIME0;
+
+#else // BX_USE_SPECIFIED_TIME0 != 0
+
+ // localtime
+ if (bx_options.clock.Otime0->get () == BX_CLOCK_TIME0_LOCAL) {
+ BX_INFO(("Using local time for initial clock"));
+ BX_CMOS_THIS s.timeval = time(NULL);
+ }
+ // utc
+ else if (bx_options.clock.Otime0->get () == BX_CLOCK_TIME0_UTC) {
+ bx_bool utc_ok = 0;
+
+ BX_INFO(("Using utc time for initial clock"));
+
+ BX_CMOS_THIS s.timeval = time(NULL);
+
+#if BX_HAVE_GMTIME
+#if BX_HAVE_MKTIME
+ struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
+ utc_holder->tm_isdst = -1;
+ utc_ok = 1;
+ BX_CMOS_THIS s.timeval = mktime(utc_holder);
+#elif BX_HAVE_TIMELOCAL
+ struct tm *utc_holder = gmtime(&BX_CMOS_THIS s.timeval);
+ utc_holder->tm_isdst = 0; // XXX Is this correct???
+ utc_ok = 1;
+ BX_CMOS_THIS s.timeval = timelocal(utc_holder);
+#endif //BX_HAVE_MKTIME
+#endif //BX_HAVE_GMTIME
+
+ if (!utc_ok) {
+ BX_ERROR(("UTC time is not supported on your platform. Using current time(NULL)"));
+ }
+ }
+ else {
+ BX_INFO(("Using specified time for initial clock"));
+ BX_CMOS_THIS s.timeval = bx_options.clock.Otime0->get ();
+ }
+#endif // BX_USE_SPECIFIED_TIME0 != 0
+
+ char *tmptime;
+ while( (tmptime = strdup(ctime(&(BX_CMOS_THIS s.timeval)))) == NULL) {
+ BX_PANIC(("Out of memory."));
+ }
+ tmptime[strlen(tmptime)-1]='\0';
+
+ BX_INFO(("Setting initial clock to: %s (time0=%u)", tmptime, (Bit32u)BX_CMOS_THIS s.timeval));
+
+ update_clock();
+ BX_CMOS_THIS s.timeval_change = 0;
+
+ // load CMOS from image file if requested.
+ if (bx_options.cmos.OcmosImage->get ()) {
+ // CMOS image file requested
+ int fd, ret;
+ struct stat stat_buf;
+
+ fd = open(bx_options.cmos.Opath->getptr (), O_RDONLY
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+ if (fd < 0) {
+ BX_PANIC(("trying to open cmos image file '%s'",
+ bx_options.cmos.Opath->getptr ()));
+ }
+ ret = fstat(fd, &stat_buf);
+ if (ret) {
+ BX_PANIC(("CMOS: could not fstat() image file."));
+ }
+ if (stat_buf.st_size != BX_NUM_CMOS_REGS) {
+ BX_PANIC(("CMOS: image file not same size as BX_NUM_CMOS_REGS."));
+ }
+
+ ret = ::read(fd, (bx_ptr_t) BX_CMOS_THIS s.reg, BX_NUM_CMOS_REGS);
+ if (ret != BX_NUM_CMOS_REGS) {
+ BX_PANIC(("CMOS: error reading cmos file."));
+ }
+ close(fd);
+ BX_INFO(("successfuly read from image file '%s'.",
+ bx_options.cmos.Opath->getptr ()));
+ }
+ else {
+ // CMOS values generated
+ BX_CMOS_THIS s.reg[REG_STAT_A] = 0x26;
+ BX_CMOS_THIS s.reg[REG_STAT_B] = 0x02;
+ BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
+ BX_CMOS_THIS s.reg[REG_STAT_D] = 0x80;
+#if BX_SUPPORT_FPU == 1
+ BX_CMOS_THIS s.reg[REG_EQUIPMENT_BYTE] |= 0x02;
+#endif
+ }
+}
+
+ void
+bx_cmos_c::reset(unsigned type)
+{
+ BX_CMOS_THIS s.cmos_mem_address = 0;
+
+ // RESET affects the following registers:
+ // CRA: no effects
+ // CRB: bits 4,5,6 forced to 0
+ // CRC: bits 4,5,6,7 forced to 0
+ // CRD: no effects
+ BX_CMOS_THIS s.reg[REG_STAT_B] &= 0x8f;
+ BX_CMOS_THIS s.reg[REG_STAT_C] = 0;
+
+ // One second timer for updating clock & alarm functions
+ bx_pc_system.activate_timer(BX_CMOS_THIS s.one_second_timer_index,
+ 1000000, 1);
+
+ // handle periodic interrupt rate select
+ BX_CMOS_THIS CRA_change();
+}
+
+ void
+bx_cmos_c::CRA_change(void)
+{
+ unsigned nibble;
+
+ // Periodic Interrupt timer
+ nibble = BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f;
+ if (nibble == 0) {
+ // No Periodic Interrupt Rate when 0, deactivate timer
+ bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
+ BX_CMOS_THIS s.periodic_interval_usec = (Bit32u) -1; // max value
+ }
+ else {
+ // values 0001b and 0010b are the same as 1000b and 1001b
+ if (nibble <= 2)
+ nibble += 7;
+ BX_CMOS_THIS s.periodic_interval_usec = (unsigned) (1000000.0L /
+ (32768.0L / (1 << (nibble - 1))));
+
+ // if Periodic Interrupt Enable bit set, activate timer
+ if ( BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40 )
+ bx_pc_system.activate_timer(BX_CMOS_THIS s.periodic_timer_index,
+ BX_CMOS_THIS s.periodic_interval_usec, 1);
+ else
+ bx_pc_system.deactivate_timer(BX_CMOS_THIS s.periodic_timer_index);
+ }
+}
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_cmos_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_CMOS_SMF
+ bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+ Bit32u
+bx_cmos_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif
+ Bit8u ret8;
+
+ if (bx_dbg.cmos)
+ BX_INFO(("CMOS read of CMOS register 0x%02x",
+ (unsigned) BX_CMOS_THIS s.cmos_mem_address));
+
+
+ switch (address) {
+ case 0x0070:
+ BX_INFO(("read of index port 0x70. returning 0xff"));
+ // Volker says his boxes return 0xff
+ //ret8 = BX_CMOS_THIS s.cmos_mem_address;
+ return(0xff);
+ break;
+ case 0x0071:
+ if (BX_CMOS_THIS s.cmos_mem_address >= BX_NUM_CMOS_REGS) {
+ BX_PANIC(("unsupported cmos io read, register(0x%02x)!",
+ (unsigned) BX_CMOS_THIS s.cmos_mem_address));
+ }
+
+ ret8 = BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address];
+ // all bits of Register C are cleared after a read occurs.
+ if (BX_CMOS_THIS s.cmos_mem_address == REG_STAT_C) {
+ BX_CMOS_THIS s.reg[REG_STAT_C] = 0x00;
+ DEV_pic_lower_irq(8);
+ }
+ return(ret8);
+ break;
+
+ default:
+ BX_PANIC(("unsupported cmos read, address=0x%04x!",
+ (unsigned) address));
+ return(0);
+ break;
+ }
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_cmos_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_CMOS_SMF
+ bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_cmos_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_CMOS_SMF
+
+ if (bx_dbg.cmos)
+ BX_INFO(("CMOS write to address: 0x%04x = 0x%02x",
+ (unsigned) address, (unsigned) value));
+
+
+ switch (address) {
+ case 0x0070:
+#if (BX_NUM_CMOS_REGS == 64)
+ BX_CMOS_THIS s.cmos_mem_address = value & 0x3F;
+#else
+ BX_CMOS_THIS s.cmos_mem_address = value & 0x7F;
+#endif
+ break;
+
+ case 0x0071:
+ if (BX_CMOS_THIS s.cmos_mem_address >= BX_NUM_CMOS_REGS) {
+ BX_PANIC(("unsupported cmos io write, register(0x%02x) = 0x%02x !",
+ (unsigned) BX_CMOS_THIS s.cmos_mem_address, (unsigned) value));
+ return;
+ }
+ switch (BX_CMOS_THIS s.cmos_mem_address) {
+ case REG_SEC_ALARM: // seconds alarm
+ case REG_MIN_ALARM: // minutes alarm
+ case REG_HOUR_ALARM: // hours alarm
+ BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
+ BX_DEBUG(("alarm time changed to %02x:%02x:%02x", BX_CMOS_THIS s.reg[REG_HOUR_ALARM],
+ BX_CMOS_THIS s.reg[REG_MIN_ALARM], BX_CMOS_THIS s.reg[REG_SEC_ALARM]));
+ return;
+ break;
+
+ case REG_SEC: // seconds
+ case REG_MIN: // minutes
+ case REG_HOUR: // hours
+ case REG_WEEK_DAY: // day of the week
+ case REG_MONTH_DAY: // day of the month
+ case REG_MONTH: // month
+ case REG_YEAR: // year
+ case REG_IBM_CENTURY_BYTE: // century
+ case REG_IBM_PS2_CENTURY_BYTE: // century (PS/2)
+ //BX_INFO(("write reg 0x%02x: value = 0x%02x",
+ // (unsigned) BX_CMOS_THIS s.cmos_mem_address, (unsigned) value);
+ BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
+ if (BX_CMOS_THIS s.cmos_mem_address == REG_IBM_PS2_CENTURY_BYTE) {
+ BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] = value;
+ }
+ if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80) {
+ BX_CMOS_THIS s.timeval_change = 1;
+ } else {
+ update_timeval();
+ }
+ return;
+ break;
+
+ case REG_STAT_A: // Control Register A
+ // bit 7: Update in Progress (read-only)
+ // 1 = signifies time registers will be updated within 244us
+ // 0 = time registers will not occur before 244us
+ // note: this bit reads 0 when CRB bit 7 is 1
+ // bit 6..4: Divider Chain Control
+ // 000 oscillator disabled
+ // 001 oscillator disabled
+ // 010 Normal operation
+ // 011 TEST
+ // 100 TEST
+ // 101 TEST
+ // 110 Divider Chain RESET
+ // 111 Divider Chain RESET
+ // bit 3..0: Periodic Interrupt Rate Select
+ // 0000 None
+ // 0001 3.90625 ms
+ // 0010 7.8125 ms
+ // 0011 122.070 us
+ // 0100 244.141 us
+ // 0101 488.281 us
+ // 0110 976.562 us
+ // 0111 1.953125 ms
+ // 1000 3.90625 ms
+ // 1001 7.8125 ms
+ // 1010 15.625 ms
+ // 1011 31.25 ms
+ // 1100 62.5 ms
+ // 1101 125 ms
+ // 1110 250 ms
+ // 1111 500 ms
+
+ unsigned dcc;
+ dcc = (value >> 4) & 0x07;
+ if ((dcc & 0x06) == 0x06) {
+ BX_INFO(("CRA: divider chain RESET"));
+ } else if (dcc != 0x02) {
+ BX_PANIC(("CRA: divider chain control 0x%02x", dcc));
+ }
+ BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x80;
+ BX_CMOS_THIS s.reg[REG_STAT_A] |= (value & 0x7f);
+ BX_CMOS_THIS CRA_change();
+ return;
+ break;
+
+ case REG_STAT_B: // Control Register B
+ // bit 0: Daylight Savings Enable
+ // 1 = enable daylight savings
+ // 0 = disable daylight savings
+ // bit 1: 24/12 houre mode
+ // 1 = 24 hour format
+ // 0 = 12 hour format
+ // bit 2: Data Mode
+ // 1 = binary format
+ // 0 = BCD format
+ // bit 3: "square wave enable"
+ // Not supported and always read as 0
+ // bit 4: Update Ended Interrupt Enable
+ // 1 = enable generation of update ended interrupt
+ // 0 = disable
+ // bit 5: Alarm Interrupt Enable
+ // 1 = enable generation of alarm interrupt
+ // 0 = disable
+ // bit 6: Periodic Interrupt Enable
+ // 1 = enable generation of periodic interrupt
+ // 0 = disable
+ // bit 7: Set mode
+ // 1 = user copy of time is "frozen" allowing time registers
+ // to be accessed without regard for an occurance of an update
+ // 0 = time updates occur normally
+
+ // can not handle binary or 12-hour mode yet.
+ if (value & 0x04)
+ BX_PANIC(("write status reg B, binary format enabled."));
+ if ( !(value & 0x02) )
+ BX_PANIC(("write status reg B, 12 hour mode enabled."));
+
+ value &= 0xf7; // bit3 always 0
+ // Note: setting bit 7 clears bit 4
+ if (value & 0x80)
+ value &= 0xef;
+
+ unsigned prev_CRB;
+ prev_CRB = BX_CMOS_THIS s.reg[REG_STAT_B];
+ BX_CMOS_THIS s.reg[REG_STAT_B] = value;
+ if ( (prev_CRB & 0x40) != (value & 0x40) ) {
+ // Periodic Interrupt Enabled changed
+ if (prev_CRB & 0x40) {
+ // transition from 1 to 0, deactivate timer
+ bx_pc_system.deactivate_timer(
+ BX_CMOS_THIS s.periodic_timer_index);
+ }
+ else {
+ // transition from 0 to 1
+ // if rate select is not 0, activate timer
+ if ( (BX_CMOS_THIS s.reg[REG_STAT_A] & 0x0f) != 0 ) {
+ bx_pc_system.activate_timer(
+ BX_CMOS_THIS s.periodic_timer_index,
+ BX_CMOS_THIS s.periodic_interval_usec, 1);
+ }
+ }
+ }
+ if ( (prev_CRB >= 0x80) && (value < 0x80) && BX_CMOS_THIS s.timeval_change) {
+ update_timeval();
+ BX_CMOS_THIS s.timeval_change = 0;
+ }
+ return;
+ break;
+
+ case REG_STAT_C: // Control Register C
+ case REG_STAT_D: // Control Register D
+ BX_ERROR(("write to control register 0x%02x (read-only)",
+ BX_CMOS_THIS s.cmos_mem_address));
+ break;
+
+ case REG_DIAGNOSTIC_STATUS:
+ BX_DEBUG(("write register 0x0e: 0x%02x", (unsigned) value));
+ break;
+
+ case REG_SHUTDOWN_STATUS:
+ switch (value) {
+ case 0x00: /* proceed with normal POST (soft reset) */
+ BX_DEBUG(("Reg 0Fh(00): shutdown action = normal POST"));
+ break;
+ case 0x01: /* shutdown after memory size check */
+ BX_DEBUG(("Reg 0Fh(01): request to change shutdown action"
+ " to shutdown after memory size check"));
+ case 0x02: /* shutdown after successful memory test */
+ BX_DEBUG(("Reg 0Fh(02): request to change shutdown action"
+ " to shutdown after successful memory test"));
+ break;
+ case 0x03: /* shutdown after failed memory test */
+ BX_DEBUG(("Reg 0Fh(03): request to change shutdown action"
+ " to shutdown after successful memory test"));
+ break;
+ case 0x04: /* jump to disk bootstrap routine */
+ BX_DEBUG(("Reg 0Fh(04): request to change shutdown action "
+ "to jump to disk bootstrap routine."));
+ break;
+ case 0x05: /* flush keyboard (issue EOI) and jump via 40h:0067h */
+ BX_DEBUG(("Reg 0Fh(05): request to change shutdown action "
+ "to flush keyboard (issue EOI) and jump via 40h:0067h."));
+ break;
+ case 0x06:
+ BX_DEBUG(("Reg 0Fh(06): Shutdown after memory test !"));
+ break;
+ case 0x07: /* reset (after failed test in virtual mode) */
+ BX_DEBUG(("Reg 0Fh(07): request to change shutdown action "
+ "to reset (after failed test in virtual mode)."));
+ break;
+ case 0x08: /* used by POST during protected-mode RAM test (return to POST) */
+ BX_DEBUG(("Reg 0Fh(08): request to change shutdown action "
+ "to return to POST (used by POST during protected-mode RAM test)."));
+ break;
+ case 0x09: /* return to BIOS extended memory block move
+ (interrupt 15h, func 87h was in progress) */
+ BX_DEBUG(("Reg 0Fh(09): request to change shutdown action "
+ "to return to BIOS extended memory block move."));
+ break;
+ case 0x0a: /* jump to DWORD pointer at 40:67 */
+ BX_DEBUG(("Reg 0Fh(0a): request to change shutdown action"
+ " to jump to DWORD at 40:67"));
+ break;
+ case 0x0b: /* iret to DWORD pointer at 40:67 */
+ BX_DEBUG(("Reg 0Fh(0b): request to change shutdown action"
+ " to iret to DWORD at 40:67"));
+ break;
+ case 0x0c: /* retf to DWORD pointer at 40:67 */
+ BX_DEBUG(("Reg 0Fh(0c): request to change shutdown action"
+ " to retf to DWORD at 40:67"));
+ break;
+ default:
+ BX_PANIC(("unsupported cmos io write to reg F, case 0x%02x!",
+ (unsigned) value));
+ break;
+ }
+ break;
+
+ default:
+ BX_DEBUG(("write reg 0x%02x: value = 0x%02x",
+ (unsigned) BX_CMOS_THIS s.cmos_mem_address, (unsigned) value));
+ break;
+ }
+
+ BX_CMOS_THIS s.reg[BX_CMOS_THIS s.cmos_mem_address] = value;
+ break;
+ }
+}
+
+
+ void
+bx_cmos_c::checksum_cmos(void)
+{
+ unsigned i;
+ Bit16u sum;
+
+ sum = 0;
+ for (i=0x10; i<=0x2d; i++) {
+ sum += BX_CMOS_THIS s.reg[i];
+ }
+ BX_CMOS_THIS s.reg[REG_CSUM_HIGH] = (sum >> 8) & 0xff; /* checksum high */
+ BX_CMOS_THIS s.reg[REG_CSUM_LOW] = (sum & 0xff); /* checksum low */
+}
+
+ void
+bx_cmos_c::periodic_timer_handler(void *this_ptr)
+{
+ bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
+
+ class_ptr->periodic_timer();
+}
+
+ void
+bx_cmos_c::periodic_timer()
+{
+ // if periodic interrupts are enabled, trip IRQ 8, and
+ // update status register C
+ if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x40) {
+ BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xc0; // Interrupt Request, Periodic Int
+ DEV_pic_raise_irq(8);
+ }
+}
+
+ void
+bx_cmos_c::one_second_timer_handler(void *this_ptr)
+{
+ bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
+
+ class_ptr->one_second_timer();
+}
+
+ void
+bx_cmos_c::one_second_timer()
+{
+ // divider chain reset - RTC stopped
+ if ((BX_CMOS_THIS s.reg[REG_STAT_A] & 0x60) == 0x60)
+ return;
+
+ // update internal time/date buffer
+ BX_CMOS_THIS s.timeval++;
+
+ // Dont update CMOS user copy of time/date if CRB bit7 is 1
+ // Nothing else do to
+ if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x80)
+ return;
+
+ BX_CMOS_THIS s.reg[REG_STAT_A] |= 0x80; // set UIP bit
+
+ // UIP timer for updating clock & alarm functions
+ bx_pc_system.activate_timer(BX_CMOS_THIS s.uip_timer_index,
+ 244, 0);
+}
+
+ void
+bx_cmos_c::uip_timer_handler(void *this_ptr)
+{
+ bx_cmos_c *class_ptr = (bx_cmos_c *) this_ptr;
+
+ class_ptr->uip_timer();
+}
+
+ void
+bx_cmos_c::uip_timer()
+{
+ update_clock();
+
+ // if update interrupts are enabled, trip IRQ 8, and
+ // update status register C
+ if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x10) {
+ BX_CMOS_THIS s.reg[REG_STAT_C] |= 0x90; // Interrupt Request, Update Ended
+ DEV_pic_raise_irq(8);
+ }
+
+ // compare CMOS user copy of time/date to alarm time/date here
+ if (BX_CMOS_THIS s.reg[REG_STAT_B] & 0x20) {
+ // Alarm interrupts enabled
+ bx_bool alarm_match = 1;
+ if ( (BX_CMOS_THIS s.reg[REG_SEC_ALARM] & 0xc0) != 0xc0 ) {
+ // seconds alarm not in dont care mode
+ if (BX_CMOS_THIS s.reg[REG_SEC] != BX_CMOS_THIS s.reg[REG_SEC_ALARM])
+ alarm_match = 0;
+ }
+ if ( (BX_CMOS_THIS s.reg[REG_MIN_ALARM] & 0xc0) != 0xc0 ) {
+ // minutes alarm not in dont care mode
+ if (BX_CMOS_THIS s.reg[REG_MIN] != BX_CMOS_THIS s.reg[REG_MIN_ALARM])
+ alarm_match = 0;
+ }
+ if ( (BX_CMOS_THIS s.reg[REG_HOUR_ALARM] & 0xc0) != 0xc0 ) {
+ // hours alarm not in dont care mode
+ if (BX_CMOS_THIS s.reg[REG_HOUR] != BX_CMOS_THIS s.reg[REG_HOUR_ALARM])
+ alarm_match = 0;
+ }
+ if (alarm_match) {
+ BX_CMOS_THIS s.reg[REG_STAT_C] |= 0xa0; // Interrupt Request, Alarm Int
+ DEV_pic_raise_irq(8);
+ }
+ }
+ BX_CMOS_THIS s.reg[REG_STAT_A] &= 0x7f; // clear UIP bit
+}
+
+
+ void
+bx_cmos_c::update_clock()
+{
+ struct tm *time_calendar;
+ unsigned year, month, day, century;
+ Bit8u val_bcd;
+
+ time_calendar = localtime(& BX_CMOS_THIS s.timeval);
+
+ // update seconds
+ val_bcd =
+ ((time_calendar->tm_sec / 10) << 4) |
+ (time_calendar->tm_sec % 10);
+ BX_CMOS_THIS s.reg[REG_SEC] = val_bcd;
+
+ // update minutes
+ val_bcd =
+ ((time_calendar->tm_min / 10) << 4) |
+ (time_calendar->tm_min % 10);
+ BX_CMOS_THIS s.reg[REG_MIN] = val_bcd;
+
+ // update hours
+ val_bcd =
+ ((time_calendar->tm_hour / 10) << 4) |
+ (time_calendar->tm_hour % 10);
+ BX_CMOS_THIS s.reg[REG_HOUR] = val_bcd;
+
+ // update day of the week
+ day = time_calendar->tm_wday + 1; // 0..6 to 1..7
+ BX_CMOS_THIS s.reg[REG_WEEK_DAY] = ((day / 10) << 4) | (day % 10);
+
+ // update day of the month
+ day = time_calendar->tm_mday;
+ BX_CMOS_THIS s.reg[REG_MONTH_DAY] = ((day / 10) << 4) | (day % 10);
+
+ // update month
+ month = time_calendar->tm_mon + 1;
+ BX_CMOS_THIS s.reg[REG_MONTH] = ((month / 10) << 4) | (month % 10);
+
+ // update year
+ year = time_calendar->tm_year % 100;
+ BX_CMOS_THIS s.reg[REG_YEAR] = ((year / 10) << 4) | (year % 10);
+
+ // update century
+ century = (time_calendar->tm_year / 100) + 19;
+ BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] =
+ ((century / 10) << 4) | (century % 10);
+
+ // Raul Hudea pointed out that some bioses also use reg 0x37 for the
+ // century byte. Tony Heller says this is critical in getting WinXP to run.
+ BX_CMOS_THIS s.reg[REG_IBM_PS2_CENTURY_BYTE] =
+ BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE];
+}
+
+ void
+bx_cmos_c::update_timeval()
+{
+ struct tm time_calendar;
+ Bit8u val_bin;
+
+ // update seconds
+ val_bin =
+ ((BX_CMOS_THIS s.reg[REG_SEC] >> 4) * 10) +
+ (BX_CMOS_THIS s.reg[REG_SEC] & 0x0f);
+ time_calendar.tm_sec = val_bin;
+
+ // update minutes
+ val_bin =
+ ((BX_CMOS_THIS s.reg[REG_MIN] >> 4) * 10) +
+ (BX_CMOS_THIS s.reg[REG_MIN] & 0x0f);
+ time_calendar.tm_min = val_bin;
+
+ // update hours
+ val_bin =
+ ((BX_CMOS_THIS s.reg[REG_HOUR] >> 4) * 10) +
+ (BX_CMOS_THIS s.reg[REG_HOUR] & 0x0f);
+ time_calendar.tm_hour = val_bin;
+
+ // update day of the month
+ val_bin =
+ ((BX_CMOS_THIS s.reg[REG_MONTH_DAY] >> 4) * 10) +
+ (BX_CMOS_THIS s.reg[REG_MONTH_DAY] & 0x0f);
+ time_calendar.tm_mday = val_bin;
+
+ // update month
+ val_bin =
+ ((BX_CMOS_THIS s.reg[REG_MONTH] >> 4) * 10) +
+ (BX_CMOS_THIS s.reg[REG_MONTH] & 0x0f);
+ time_calendar.tm_mon = val_bin - 1;
+
+ // update year
+ val_bin =
+ ((BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] >> 4) * 10) +
+ (BX_CMOS_THIS s.reg[REG_IBM_CENTURY_BYTE] & 0x0f);
+ val_bin = (val_bin - 19) * 100;
+ val_bin +=
+ (((BX_CMOS_THIS s.reg[REG_YEAR] >> 4) * 10) +
+ (BX_CMOS_THIS s.reg[REG_YEAR] & 0x0f));
+ time_calendar.tm_year = val_bin;
+
+ BX_CMOS_THIS s.timeval = mktime(& time_calendar);
+}
diff --git a/tools/ioemu/iodev/cmos.h b/tools/ioemu/iodev/cmos.h
new file mode 100644
index 0000000000..c53907db3e
--- /dev/null
+++ b/tools/ioemu/iodev/cmos.h
@@ -0,0 +1,90 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: cmos.h,v 1.9 2003/01/04 00:02:07 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+
+#if BX_USE_CMOS_SMF
+# define BX_CMOS_SMF static
+# define BX_CMOS_THIS theCmosDevice->
+#else
+# define BX_CMOS_SMF
+# define BX_CMOS_THIS this->
+#endif
+
+
+class bx_cmos_c : public bx_cmos_stub_c {
+public:
+ bx_cmos_c(void);
+ ~bx_cmos_c(void);
+
+ virtual void init(void);
+ virtual void checksum_cmos(void);
+ virtual void reset(unsigned type);
+
+ virtual Bit32u get_reg(unsigned reg) {
+ return s.reg[reg];
+ }
+ virtual void set_reg(unsigned reg, Bit32u val) {
+ s.reg[reg] = val;
+ }
+ virtual time_t get_timeval() {
+ return s.timeval;
+ }
+
+ struct {
+ int periodic_timer_index;
+ Bit32u periodic_interval_usec;
+ int one_second_timer_index;
+ int uip_timer_index;
+ time_t timeval;
+ Bit8u cmos_mem_address;
+ bx_bool timeval_change;
+
+ Bit8u reg[BX_NUM_CMOS_REGS];
+ } s; // state information
+
+private:
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_CMOS_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned len);
+#endif
+
+public:
+ static void periodic_timer_handler(void *);
+ static void one_second_timer_handler(void *);
+ static void uip_timer_handler(void *);
+ BX_CMOS_SMF void periodic_timer(void);
+ BX_CMOS_SMF void one_second_timer(void);
+ BX_CMOS_SMF void uip_timer(void);
+private:
+ BX_CMOS_SMF void update_clock(void);
+ BX_CMOS_SMF void update_timeval(void);
+ BX_CMOS_SMF void CRA_change(void);
+ };
diff --git a/tools/ioemu/iodev/cpu.cc b/tools/ioemu/iodev/cpu.cc
new file mode 100644
index 0000000000..59e4c51c05
--- /dev/null
+++ b/tools/ioemu/iodev/cpu.cc
@@ -0,0 +1,334 @@
+/*
+ * Main cpu loop for handling I/O requests coming from a virtual machine
+ * Copyright © 2004, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "bochs.h"
+#include <cpu/cpu.h>
+#ifdef BX_USE_VMX
+#include <sys/ioctl.h>
+/* According to POSIX 1003.1-2001 */
+#include <sys/select.h>
+
+/* According to earlier standards */
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <values.h>
+#endif
+
+#define LOG_THIS BX_CPU_THIS_PTR
+
+#ifdef BX_USE_VMX
+
+//the evtchn fd for polling
+int evtchn_fd = -1;
+//the evtchn port for polling the notification, should be inputed as bochs's parameter
+u16 ioreq_port = 0;
+
+void *shared_page = NULL;
+
+//some functions to handle the io req packet
+
+//get the ioreq packets from share mem
+ioreq_t* bx_cpu_c::__get_ioreq(void)
+{
+ ioreq_t *req;
+ req = &((vcpu_iodata_t *) shared_page)->vp_ioreq;
+ if (req->state == STATE_IOREQ_READY) {
+ req->state = STATE_IOREQ_INPROCESS;
+ } else {
+ BX_INFO(("False I/O requrest ... in-service already: %lx, pvalid: %lx,port: %lx, data: %lx, count: %lx, size: %lx\n", req->state, req->pdata_valid, req->addr, req->u.data, req->count, req->size));
+ req = NULL;
+ }
+
+ return req;
+}
+
+//use poll to get the port notification
+//ioreq_vec--out,the
+//retval--the number of ioreq packet
+ioreq_t* bx_cpu_c::get_ioreq(void)
+{
+ ioreq_t *req;
+ int rc;
+ u16 buf[2];
+ rc = read(evtchn_fd, buf, 2);
+ if (rc == 2 && buf[0] == ioreq_port){//got only one matched 16bit port index
+ // unmask the wanted port again
+ write(evtchn_fd, &ioreq_port, 2);
+
+ //get the io packet from shared memory
+ return __get_ioreq();
+ }
+
+ //read error or read nothing
+ return NULL;
+}
+
+//send the ioreq to device model
+void bx_cpu_c::dispatch_ioreq(ioreq_t *req)
+{
+ int ret, i;
+ int sign;
+
+ sign = (req->df) ? -1 : 1;
+
+ if ((!req->pdata_valid) && (req->dir == IOREQ_WRITE)) {
+ if (req->size != 4) {
+ // Bochs expects higher bits to be 0
+ req->u.data &= (1UL << (8 * req->size))-1;
+ }
+ }
+ if (req->port_mm == 0){//port io
+ if(req->dir == IOREQ_READ){//read
+ if (!req->pdata_valid)
+ req->u.data = BX_INP(req->addr, req->size);
+ else {
+ unsigned long tmp;
+
+ for (i = 0; i < req->count; i++) {
+ tmp = BX_INP(req->addr, req->size);
+ BX_MEM_WRITE_PHYSICAL((Bit32u) req->u.pdata + (sign * i * req->size),
+ req->size, &tmp);
+ }
+ }
+ } else if(req->dir == IOREQ_WRITE) {
+ if (!req->pdata_valid) {
+ BX_OUTP(req->addr, (Bit32u) req->u.data, req->size);
+ } else {
+ for (i = 0; i < req->count; i++) {
+ unsigned long tmp;
+
+ BX_MEM_READ_PHYSICAL((Bit32u) req->u.pdata + (sign * i * req->size), req->size,
+ &tmp);
+ BX_OUTP(req->addr, (Bit32u) tmp, req->size);
+ }
+ }
+
+ }
+ } else if (req->port_mm == 1){//memory map io
+ if (!req->pdata_valid) {
+ if(req->dir == IOREQ_READ){//read
+ BX_MEM_READ_PHYSICAL(req->addr, req->size, &req->u.data);
+ } else if(req->dir == IOREQ_WRITE)//write
+ BX_MEM_WRITE_PHYSICAL(req->addr, req->size, &req->u.data);
+ } else {
+ //handle movs
+ unsigned long tmp;
+ if (req->dir == IOREQ_READ) {
+ //BX_INFO(("<READ>addr:%llx, pdata:%llx, size: %x, count: %x\n", req->addr, req->u.pdata, req->size, req->count));
+ for (i = 0; i < req->count; i++) {
+ BX_MEM_READ_PHYSICAL(req->addr + (sign * i * req->size), req->size, &tmp);
+ BX_MEM_WRITE_PHYSICAL((Bit32u) req->u.pdata + (sign * i * req->size), req->size, &tmp);
+ }
+ } else if (req->dir == IOREQ_WRITE) {
+ //BX_INFO(("<WRITE>addr:%llx, pdata:%llx, size: %x, count: %x\n", req->addr, req->u.pdata, req->size, req->count));
+ for (i = 0; i < req->count; i++) {
+ BX_MEM_READ_PHYSICAL((Bit32u)req->u.pdata + (sign * i * req->size), req->size, &tmp);
+ BX_MEM_WRITE_PHYSICAL(req->addr + (sign * i * req->size), req->size, &tmp);
+ }
+ }
+ }
+ }
+
+ /* No state change if state = STATE_IORESP_HOOK */
+ if (req->state == STATE_IOREQ_INPROCESS)
+ req->state = STATE_IORESP_READY;
+
+ send_event = 1;
+}
+
+void
+bx_cpu_c::handle_ioreq(void)
+{
+ ioreq_t *req = get_ioreq();
+ if (req)
+ dispatch_ioreq(req);
+}
+
+void
+bx_cpu_c::timer_handler(void)
+{
+ handle_ioreq();
+}
+
+#endif
+
+#define rdtscl(low) \
+ __asm__ __volatile__("rdtsc" : "=a" (low) : : "edx")
+
+void
+bx_cpu_c::cpu_loop(int max_instr_count)
+{
+ Bit8u vector;
+ fd_set rfds;
+ unsigned long stime_usec;
+ struct timeval tv;
+ int retval;
+
+ /* Watch stdin (fd 0) to see when it has input. */
+ FD_ZERO(&rfds);
+
+ while (1) {
+ static unsigned long long t1 = 0;
+ unsigned long long t2;
+
+ /* Wait up to one seconds. */
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ FD_SET(evtchn_fd, &rfds);
+
+ send_event = 0;
+
+ if (t1 == 0) // the first time
+ rdtscll(t1);
+
+ retval = select(evtchn_fd+1, &rfds, NULL, NULL, &tv);
+ if (retval == -1) {
+ perror("select");
+ return;
+ }
+
+ rdtscll(t2);
+
+#if __WORDSIZE == 32
+#define ULONGLONG_MAX 0xffffffffffffffffULL
+#else
+#define ULONGLONG_MAX ULONG_MAX
+#endif
+
+ if (t2 <= t1)
+ BX_TICKN((t2 + ULONGLONG_MAX - t1) / tsc_per_bx_tick);
+ else
+ BX_TICKN((t2 - t1) / tsc_per_bx_tick);
+ t1 = t2;
+
+ timer_handler();
+ if (BX_CPU_INTR) {
+#if BX_SUPPORT_APIC
+ if (BX_CPU_THIS_PTR local_apic.INTR)
+ vector = BX_CPU_THIS_PTR local_apic.acknowledge_int ();
+ else
+ vector = DEV_pic_iac(); // may set INTR with next interrupt
+#else
+ // if no local APIC, always acknowledge the PIC.
+ vector = DEV_pic_iac(); // may set INTR with next interrupt
+#endif
+ interrupt(vector);
+ }
+ /* we check DMA after interrupt check*/
+ while(BX_HRQ){
+ DEV_dma_raise_hlda();
+ }
+
+ if (send_event) {
+ int ret;
+ ret = xc_evtchn_send(xc_handle, ioreq_port);
+ if (ret == -1) {
+ BX_ERROR(("evtchn_send failed on port: %d\n", ioreq_port));
+ }
+ }
+ }
+}
+
+static __inline__ void set_bit(long nr, volatile void *addr)
+{
+ __asm__ __volatile__( "lock ; "
+ "btsl %1,%0"
+ :"=m" ((*(volatile long *)addr))
+ :"Ir" (nr));
+
+ return;
+}
+
+void
+bx_cpu_c::interrupt(Bit8u vector)
+{
+ unsigned long *intr, tscl;
+ int ret;
+
+ // Send a message on the event channel. Add the vector to the shared mem
+ // page.
+
+ rdtscl(tscl);
+ BX_DEBUG(("%lx: injecting vector: %x\n", tscl, vector));
+ intr = &(((vcpu_iodata_t *) shared_page)->vp_intr[0]);
+ set_bit(vector, intr);
+
+ send_event = 1;
+}
+
+void
+bx_cpu_c::init(bx_mem_c*)
+{
+#ifdef BX_USE_VMX
+ if (evtchn_fd != -1)//the evtchn has been opened by another cpu object
+ return;
+
+ //use nonblock reading not polling, may change in future.
+ evtchn_fd = open("/dev/xen/evtchn", O_RDWR|O_NONBLOCK);
+ if (evtchn_fd == -1) {
+ perror("open");
+ return;
+ }
+
+ BX_INFO(("listening to port: %d\n", ioreq_port));
+ /*unmask the wanted port -- bind*/
+ if (ioctl(evtchn_fd, ('E'<<8)|2, ioreq_port) == -1) {
+ perror("ioctl");
+ return;
+ }
+
+#if 0
+ //register the reading evtchn function as timer
+ bx_pc_system.register_timer(this, timer_handler, 1000,//1000 us, may change
+ 1,//continuous timer
+ 1,//active
+ "cpu reading evtchn handler");
+#endif
+
+#endif
+}
+
+void
+bx_cpu_c::reset(unsigned)
+{
+}
+
+void
+bx_cpu_c::atexit()
+{
+}
+
+void
+bx_cpu_c::set_INTR(unsigned value)
+{
+ BX_CPU_THIS_PTR INTR = value;
+}
+
+void
+bx_cpu_c::pagingA20Changed()
+{
+}
+
+bx_cpu_c::bx_cpu_c()
+{
+}
+
+bx_cpu_c::~bx_cpu_c()
+{
+}
diff --git a/tools/ioemu/iodev/crc32.cc b/tools/ioemu/iodev/crc32.cc
new file mode 100644
index 0000000000..a20abaaf82
--- /dev/null
+++ b/tools/ioemu/iodev/crc32.cc
@@ -0,0 +1,49 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: crc32.cc,v 1.4 2001/10/03 13:10:38 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+/* CRC-32 calculator
+ * Adapted from http://www.createwindow.org/programming/crc32/
+ */
+
+#include "crc32.h"
+
+CRC_Generator::CRC_Generator() {
+ init();
+}
+
+void CRC_Generator::init(void) {
+ Bit32u POLYNOMIAL = 0x04c11db7;
+ int i;
+
+ for(i = 0; i<0xFF; i++) {
+ int j;
+ crc32_table[i]=reflect(i,8) << 24;
+ for(j=0; j<8; j++)
+ crc32_table[i] = (crc32_table[i]<<1)^(crc32_table[i] & (1<<31) ? POLYNOMIAL : 0);
+ crc32_table[i] = reflect(crc32_table[i], 32);
+ }
+}
+
+Bit32u CRC_Generator::reflect(Bit32u ref, Bit8u ch) {
+ Bit32u value(0);
+ int i;
+
+ for(i=1; i<(ch+1); i++) {
+ if(ref & 1)
+ value |= 1 << (ch-i);
+ ref >>= 1;
+ }
+ return value;
+}
+
+Bit32u CRC_Generator::get_CRC(Bit8u * buf, Bit32u buflen) {
+ Bit32u ulCRC(0xFFFFFFFF);
+ Bit32u len(buflen);
+ Bit8u * buffer=(Bit8u *) buf;
+
+ while(len--)
+ ulCRC=(ulCRC>>8)^crc32_table[(ulCRC & 0xFF)^*buffer++];
+ return ulCRC ^ 0xFFFFFFFF;
+}
+
diff --git a/tools/ioemu/iodev/crc32.h b/tools/ioemu/iodev/crc32.h
new file mode 100644
index 0000000000..faedaf840b
--- /dev/null
+++ b/tools/ioemu/iodev/crc32.h
@@ -0,0 +1,25 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: crc32.h,v 1.3 2001/10/03 13:10:38 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+/* CRC-32 calculator
+ * Adapted from http://www.createwindow.org/programming/crc32/
+ */
+
+#ifndef _CRC_32_H_
+#define _CRC_32_H_
+
+#include "bochs.h"
+
+class CRC_Generator {
+private:
+ Bit32u crc32_table[256];
+ Bit32u reflect(Bit32u ref, Bit8u ch);
+public:
+ void init(void);
+ CRC_Generator();
+ Bit32u get_CRC(Bit8u * buf, Bit32u buflen);
+};
+
+#endif //_CRC_32_H_
+
diff --git a/tools/ioemu/iodev/devices.cc b/tools/ioemu/iodev/devices.cc
new file mode 100644
index 0000000000..5dd7f5daa3
--- /dev/null
+++ b/tools/ioemu/iodev/devices.cc
@@ -0,0 +1,685 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: devices.cc,v 1.58 2003/12/26 13:53:39 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#include "bochs.h"
+#define LOG_THIS bx_devices.
+
+
+
+/* main memory size (in Kbytes)
+ * subtract 1k for extended BIOS area
+ * report only base memory, not extended mem
+ */
+#define BASE_MEMORY_IN_K 640
+
+
+bx_devices_c bx_devices;
+
+
+
+
+// constructor for bx_devices_c
+bx_devices_c::bx_devices_c(void)
+{
+ put("DEV");
+ settype(DEVLOG);
+
+#if BX_PCI_SUPPORT
+ pluginPciBridge = &stubPci;
+ pluginPci2IsaBridge = NULL;
+#if BX_PCI_VGA_SUPPORT
+ pluginPciVgaAdapter = NULL;
+#endif
+#if BX_PCI_USB_SUPPORT
+ pluginPciUSBAdapter = NULL;
+#endif
+#endif
+ pit = NULL;
+ pluginKeyboard = &stubKeyboard;
+ pluginDmaDevice = &stubDma;
+ pluginFloppyDevice = &stubFloppy;
+ pluginBiosDevice = NULL;
+ pluginCmosDevice = &stubCmos;
+ pluginSerialDevice = NULL;
+ pluginParallelDevice = NULL;
+ pluginUnmapped = NULL;
+ pluginVgaDevice = &stubVga;
+ pluginPicDevice = &stubPic;
+ pluginHardDrive = &stubHardDrive;
+ pluginSB16Device = NULL;
+ pluginNE2kDevice =&stubNE2k;
+ pluginExtFpuIrq = NULL;
+ pluginGameport = NULL;
+ g2h = NULL;
+#if BX_IODEBUG_SUPPORT
+ iodebug = NULL;
+#endif
+}
+
+
+bx_devices_c::~bx_devices_c(void)
+{
+ // nothing needed for now
+ BX_DEBUG(("Exit."));
+ timer_handle = BX_NULL_TIMER_HANDLE;
+}
+
+
+ void
+bx_devices_c::init(BX_MEM_C *newmem)
+{
+ unsigned i;
+
+ BX_DEBUG(("Init $Id: devices.cc,v 1.58 2003/12/26 13:53:39 vruppert Exp $"));
+ mem = newmem;
+
+ /* no read / write handlers defined */
+ num_read_handles = 0;
+ num_write_handles = 0;
+
+ /* set unused elements to appropriate values */
+ for (i=0; i < BX_MAX_IO_DEVICES; i++) {
+ io_read_handler[i].funct = NULL;
+ io_write_handler[i].funct = NULL;
+ }
+
+ /* set no-default handlers, will be overwritten by the real default handler */
+ io_read_handler[BX_DEFAULT_IO_DEVICE].handler_name = "Default";
+ io_read_handler[BX_DEFAULT_IO_DEVICE].funct = &default_read_handler;
+ io_read_handler[BX_DEFAULT_IO_DEVICE].this_ptr = NULL;
+ io_read_handler[BX_DEFAULT_IO_DEVICE].mask = 7;
+ io_write_handler[BX_DEFAULT_IO_DEVICE].handler_name = "Default";
+ io_write_handler[BX_DEFAULT_IO_DEVICE].funct = &default_write_handler;
+ io_write_handler[BX_DEFAULT_IO_DEVICE].this_ptr = NULL;
+ io_write_handler[BX_DEFAULT_IO_DEVICE].mask = 7;
+
+ /* set handlers to the default one */
+ for (i=0; i < 0x10000; i++) {
+ read_handler_id[i] = BX_DEFAULT_IO_DEVICE;
+ write_handler_id[i] = BX_DEFAULT_IO_DEVICE;
+ }
+
+ for (i=0; i < BX_MAX_IRQS; i++) {
+ irq_handler_name[i] = NULL;
+ }
+
+ // BBD: At present, the only difference between "core" and "optional"
+ // plugins is that initialization and reset of optional plugins is handled
+ // by the plugin device list (). Init and reset of core plugins is done
+ // "by hand" in this file. Basically, we're using core plugins when we
+ // want to control the init order.
+ //
+ // CB: UNMAPPED and BIOSDEV should maybe be optional
+ PLUG_load_plugin(unmapped, PLUGTYPE_CORE);
+ PLUG_load_plugin(biosdev, PLUGTYPE_CORE);
+ PLUG_load_plugin(cmos, PLUGTYPE_CORE);
+ PLUG_load_plugin(dma, PLUGTYPE_CORE);
+ PLUG_load_plugin(pic, PLUGTYPE_CORE);
+ PLUG_load_plugin(vga, PLUGTYPE_CORE);
+ PLUG_load_plugin(floppy, PLUGTYPE_CORE);
+ PLUG_load_plugin(harddrv, PLUGTYPE_OPTIONAL);
+ PLUG_load_plugin(keyboard, PLUGTYPE_OPTIONAL);
+ if (is_serial_enabled ())
+ PLUG_load_plugin(serial, PLUGTYPE_OPTIONAL);
+ if (is_parallel_enabled ())
+ PLUG_load_plugin(parallel, PLUGTYPE_OPTIONAL);
+ PLUG_load_plugin(extfpuirq, PLUGTYPE_OPTIONAL);
+#if BX_SUPPORT_GAME
+ PLUG_load_plugin(gameport, PLUGTYPE_OPTIONAL);
+#endif
+
+ // Start with registering the default (unmapped) handler
+ pluginUnmapped->init ();
+
+ // PCI logic (i440FX)
+ if (bx_options.Oi440FXSupport->get ()) {
+#if BX_PCI_SUPPORT
+ PLUG_load_plugin(pci, PLUGTYPE_OPTIONAL);
+ PLUG_load_plugin(pci2isa, PLUGTYPE_OPTIONAL);
+#if BX_PCI_VGA_SUPPORT
+ PLUG_load_plugin(pcivga, PLUGTYPE_OPTIONAL);
+#endif
+#if BX_PCI_USB_SUPPORT
+ PLUG_load_plugin(pciusb, PLUGTYPE_OPTIONAL);
+#endif
+#else
+ BX_ERROR(("Bochs is not compiled with PCI support"));
+#endif
+ }
+
+#if BX_SUPPORT_APIC
+ // I/O APIC 82093AA
+ ioapic = & bx_ioapic;
+ ioapic->init ();
+#endif
+
+ // BIOS log
+ pluginBiosDevice->init ();
+
+ // CMOS RAM & RTC
+ pluginCmosDevice->init ();
+
+ /*--- 8237 DMA ---*/
+ pluginDmaDevice->init();
+
+ //--- FLOPPY ---
+ pluginFloppyDevice->init();
+
+ //--- SOUND ---
+ if (bx_options.sb16.Opresent->get ()) {
+#if BX_SUPPORT_SB16
+ PLUG_load_plugin(sb16, PLUGTYPE_OPTIONAL);
+#else
+ BX_ERROR(("Bochs is not compiled with SB16 support"));
+#endif
+ }
+
+ /*--- VGA adapter ---*/
+ pluginVgaDevice->init ();
+
+ /*--- 8259A PIC ---*/
+ pluginPicDevice->init();
+
+ /*--- 8254 PIT ---*/
+ pit = & bx_pit;
+ pit->init();
+
+ bx_virt_timer.init();
+
+ bx_slowdown_timer.init();
+
+#if BX_IODEBUG_SUPPORT
+ iodebug = &bx_iodebug;
+ iodebug->init();
+#endif
+
+ // NE2000 NIC
+ if (bx_options.ne2k.Opresent->get ()) {
+#if BX_NE2K_SUPPORT
+ PLUG_load_plugin(ne2k, PLUGTYPE_OPTIONAL);
+#else
+ BX_ERROR(("Bochs is not compiled with NE2K support"));
+#endif
+ }
+
+#if 0
+ // Guest to Host interface. Used with special guest drivers
+ // which move data to/from the host environment.
+ g2h = &bx_g2h;
+ g2h->init();
+#endif
+
+ // system hardware
+ register_io_read_handler( this,
+ &read_handler,
+ 0x0092,
+ "Port 92h System Control", 1 );
+ register_io_write_handler(this,
+ &write_handler,
+ 0x0092,
+ "Port 92h System Control", 1 );
+
+ // misc. CMOS
+ Bit32u extended_memory_in_k = mem->get_memory_in_k() > 1024 ? (mem->get_memory_in_k() - 1024) : 0;
+ if (extended_memory_in_k > 0xffff) extended_memory_in_k = 0xffff;
+
+ DEV_cmos_set_reg(0x15, (Bit8u) BASE_MEMORY_IN_K);
+ DEV_cmos_set_reg(0x16, (Bit8u) (BASE_MEMORY_IN_K >> 8));
+ DEV_cmos_set_reg(0x17, (Bit8u) (extended_memory_in_k & 0xff) );
+ DEV_cmos_set_reg(0x18, (Bit8u) ((extended_memory_in_k >> 8) & 0xff) );
+ DEV_cmos_set_reg(0x30, (Bit8u) (extended_memory_in_k & 0xff) );
+ DEV_cmos_set_reg(0x31, (Bit8u) ((extended_memory_in_k >> 8) & 0xff) );
+
+ Bit32u extended_memory_in_64k = mem->get_memory_in_k() > 16384 ? (mem->get_memory_in_k() - 16384) / 64 : 0;
+ if (extended_memory_in_64k > 0xffff) extended_memory_in_64k = 0xffff;
+
+ DEV_cmos_set_reg(0x34, (Bit8u) (extended_memory_in_64k & 0xff) );
+ DEV_cmos_set_reg(0x35, (Bit8u) ((extended_memory_in_64k >> 8) & 0xff) );
+
+ if (timer_handle != BX_NULL_TIMER_HANDLE) {
+ timer_handle = bx_pc_system.register_timer( this, timer_handler,
+ (unsigned) BX_IODEV_HANDLER_PERIOD, 1, 1, "devices.cc");
+ }
+
+ // Clear fields for bulk IO acceleration transfers.
+ bulkIOHostAddr = 0;
+ bulkIOQuantumsRequested = 0;
+ bulkIOQuantumsTransferred = 0;
+
+ bx_init_plugins();
+
+ /* now perform checksum of CMOS memory */
+ DEV_cmos_checksum();
+}
+
+
+ void
+bx_devices_c::reset(unsigned type)
+{
+ pluginUnmapped->reset(type);
+#if BX_PCI_SUPPORT
+ if (bx_options.Oi440FXSupport->get ()) {
+ pluginPciBridge->reset(type);
+ pluginPci2IsaBridge->reset(type);
+#if BX_PCI_VGA_SUPPORT
+ pluginPciVgaAdapter->reset(type);
+#endif
+#if BX_PCI_USB_SUPPORT
+ pluginPciUSBAdapter->reset(type);
+#endif
+ }
+#endif
+#if BX_SUPPORT_IOAPIC
+ ioapic->reset (type);
+#endif
+ pluginBiosDevice->reset(type);
+ pluginCmosDevice->reset(type);
+ pluginDmaDevice->reset(type);
+ pluginFloppyDevice->reset(type);
+#if BX_SUPPORT_SB16
+ if (pluginSB16Device) pluginSB16Device->reset(type);
+#endif
+ pluginVgaDevice->reset(type);
+ pluginPicDevice->reset(type);
+ pit->reset(type);
+ bx_slowdown_timer.reset(type);
+#if BX_IODEBUG_SUPPORT
+ iodebug->reset(type);
+#endif
+#if BX_NE2K_SUPPORT
+ if (pluginNE2kDevice) pluginNE2kDevice->reset(type);
+#endif
+
+ bx_reset_plugins(type);
+}
+
+
+ Bit32u
+bx_devices_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_DEV_SMF
+ bx_devices_c *class_ptr = (bx_devices_c *) this_ptr;
+
+ return( class_ptr->port92_read(address, io_len) );
+}
+
+
+ Bit32u
+bx_devices_c::port92_read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_DEV_SMF
+
+ BX_DEBUG(("port92h read partially supported!!!"));
+ BX_DEBUG((" returning %02x", (unsigned) (BX_GET_ENABLE_A20() << 1)));
+ return(BX_GET_ENABLE_A20() << 1);
+}
+
+
+ void
+bx_devices_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_DEV_SMF
+ bx_devices_c *class_ptr = (bx_devices_c *) this_ptr;
+
+ class_ptr->port92_write(address, value, io_len);
+}
+
+ void
+bx_devices_c::port92_write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_DEV_SMF
+ bx_bool bx_cpu_reset;
+
+ BX_DEBUG(("port92h write of %02x partially supported!!!",
+ (unsigned) value));
+ BX_DEBUG(("A20: set_enable_a20() called"));
+ BX_SET_ENABLE_A20( (value & 0x02) >> 1 );
+ BX_DEBUG(("A20: now %u", (unsigned) BX_GET_ENABLE_A20()));
+ bx_cpu_reset = (value & 0x01); /* high speed reset */
+ if (bx_cpu_reset) {
+ BX_PANIC(("PORT 92h write: CPU reset requested!"));
+ }
+}
+
+
+// This defines a no-default read handler,
+// so Bochs does not segfault if unmapped is not loaded
+ Bit32u
+bx_devices_c::default_read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+ UNUSED(this_ptr);
+ BX_PANIC(("No default io-read handler found for 0x%04x/%d. Unmapped io-device not loaded ?", address, io_len));
+ return 0xffffffff;
+}
+
+// This defines a no-default write handler,
+// so Bochs does not segfault if unmapped is not loaded
+ void
+bx_devices_c::default_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+ UNUSED(this_ptr);
+ BX_PANIC(("No default io-write handler found for 0x%04x/%d. Unmapped io-device not loaded ?", address, io_len));
+}
+
+ void
+bx_devices_c::timer_handler(void *this_ptr)
+{
+ bx_devices_c *class_ptr = (bx_devices_c *) this_ptr;
+
+ class_ptr->timer();
+}
+
+ void
+bx_devices_c::timer()
+{
+#if (BX_USE_NEW_PIT==0)
+ if ( pit->periodic( BX_IODEV_HANDLER_PERIOD ) ) {
+ // This is a hack to make the IRQ0 work
+ DEV_pic_lower_irq(0);
+ DEV_pic_raise_irq(0);
+ }
+#endif
+
+
+ // separate calls to bx_gui->handle_events from the keyboard code.
+ {
+ static int multiple=0;
+ if ( ++multiple==10)
+ {
+ multiple=0;
+ SIM->periodic ();
+ if (!BX_CPU(0)->kill_bochs_request)
+ bx_gui->handle_events();
+ }
+ }
+
+// KPL Removed lapic periodic timer registration here.
+}
+
+
+ bx_bool
+bx_devices_c::register_irq(unsigned irq, const char *name)
+{
+ if (irq >= BX_MAX_IRQS) {
+ BX_PANIC(("IO device %s registered with IRQ=%d above %u",
+ name, irq, (unsigned) BX_MAX_IRQS-1));
+ return false;
+ }
+ if (irq_handler_name[irq]) {
+ BX_PANIC(("IRQ %u conflict, %s with %s", irq,
+ irq_handler_name[irq], name));
+ return false;
+ }
+ irq_handler_name[irq] = name;
+ return true;
+}
+
+ bx_bool
+bx_devices_c::unregister_irq(unsigned irq, const char *name)
+{
+ if (irq >= BX_MAX_IRQS) {
+ BX_PANIC(("IO device %s tried to unregister IRQ %d above %u",
+ name, irq, (unsigned) BX_MAX_IRQS-1));
+ return false;
+ }
+
+ if (!irq_handler_name[irq]) {
+ BX_INFO(("IO device %s tried to unregister IRQ %d, not registered",
+ name, irq));
+ return false;
+ }
+
+ if (strcmp(irq_handler_name[irq], name)) {
+ BX_INFO(("IRQ %u not registered to %s but to %s", irq,
+ name, irq_handler_name[irq]));
+ return false;
+ }
+ irq_handler_name[irq] = NULL;
+ return true;
+}
+
+ bx_bool
+bx_devices_c::register_io_read_handler( void *this_ptr, bx_read_handler_t f,
+ Bit32u addr, const char *name, Bit8u mask )
+{
+ unsigned handle;
+
+ addr &= 0x0000ffff;
+
+ /* first find existing handle for function or create new one */
+ for (handle=0; handle < num_read_handles; handle++) {
+ if ((io_read_handler[handle].funct == f) &&
+ (io_read_handler[handle].mask == mask)) break;
+ }
+
+ if (handle >= num_read_handles) {
+ /* no existing handle found, create new one */
+ if (num_read_handles >= BX_DEFAULT_IO_DEVICE) {
+ BX_INFO(("too many IO devices installed."));
+ BX_PANIC((" try increasing BX_MAX_IO_DEVICES"));
+ }
+ num_read_handles++;
+ io_read_handler[handle].funct = f;
+ io_read_handler[handle].this_ptr = this_ptr;
+ io_read_handler[handle].handler_name = name;
+ io_read_handler[handle].mask = mask;
+ }
+
+ /* change table to reflect new handler id for that address */
+ if (read_handler_id[addr] < BX_DEFAULT_IO_DEVICE) {
+ // another handler is already registered for that address
+ BX_ERROR(("IO device address conflict(read) at IO address %Xh",
+ (unsigned) addr));
+ BX_ERROR((" conflicting devices: %s & %s",
+ io_read_handler[handle].handler_name, io_read_handler[read_handler_id[addr]].handler_name));
+ return false; // address not available, return false.
+ }
+ read_handler_id[addr] = handle;
+ return true; // address mapped successfully
+}
+
+
+
+ bx_bool
+bx_devices_c::register_io_write_handler( void *this_ptr, bx_write_handler_t f,
+ Bit32u addr, const char *name, Bit8u mask )
+{
+ unsigned handle;
+
+ addr &= 0x0000ffff;
+
+ /* first find existing handle for function or create new one */
+ for (handle=0; handle < num_write_handles; handle++) {
+ if ((io_write_handler[handle].funct == f) &&
+ (io_write_handler[handle].mask == mask)) break;
+ }
+
+ if (handle >= num_write_handles) {
+ /* no existing handle found, create new one */
+ if (num_write_handles >= BX_DEFAULT_IO_DEVICE) {
+ BX_INFO(("too many IO devices installed."));
+ BX_PANIC((" try increasing BX_MAX_IO_DEVICES"));
+ }
+ num_write_handles++;
+ io_write_handler[handle].funct = f;
+ io_write_handler[handle].this_ptr = this_ptr;
+ io_write_handler[handle].handler_name = name;
+ io_write_handler[handle].mask = mask;
+ }
+
+ /* change table to reflect new handler id for that address */
+ if (write_handler_id[addr] < BX_DEFAULT_IO_DEVICE) {
+ // another handler is already registered for that address
+ BX_ERROR(("IO device address conflict(write) at IO address %Xh",
+ (unsigned) addr));
+ BX_ERROR((" conflicting devices: %s & %s",
+ io_write_handler[handle].handler_name, io_write_handler[write_handler_id[addr]].handler_name));
+ return false; //unable to map iodevice.
+ }
+ write_handler_id[addr] = handle;
+ return true; // done!
+}
+
+
+// Registration of default handlers (mainly be the unmapped device)
+// The trick here is to define a handler for the max index, so
+// unregisterd io address will get handled by the default function
+// This will be helpful when we want to unregister io handlers
+
+ bx_bool
+bx_devices_c::register_default_io_read_handler( void *this_ptr, bx_read_handler_t f,
+ const char *name, Bit8u mask )
+{
+ unsigned handle;
+
+ /* handle is fixed to the default I/O device */
+ handle = BX_DEFAULT_IO_DEVICE;
+
+ if (strcmp(io_read_handler[handle].handler_name, "Default")) {
+ BX_ERROR(("Default io read handler already registered '%s'",io_read_handler[handle].handler_name));
+ return false;
+ }
+
+ io_read_handler[handle].funct = f;
+ io_read_handler[handle].this_ptr = this_ptr;
+ io_read_handler[handle].handler_name = name;
+ io_read_handler[handle].mask = mask;
+
+ return true;
+}
+
+
+
+ bx_bool
+bx_devices_c::register_default_io_write_handler( void *this_ptr, bx_write_handler_t f,
+ const char *name, Bit8u mask )
+{
+ unsigned handle;
+
+ /* handle is fixed to the MAX */
+ handle = BX_DEFAULT_IO_DEVICE;
+
+ if (strcmp(io_write_handler[handle].handler_name, "Default")) {
+ BX_ERROR(("Default io write handler already registered '%s'",io_write_handler[handle].handler_name));
+ return false;
+ }
+
+ io_write_handler[handle].funct = f;
+ io_write_handler[handle].this_ptr = this_ptr;
+ io_write_handler[handle].handler_name = name;
+ io_write_handler[handle].mask = mask;
+
+ return true;
+}
+
+
+
+/*
+ * Read a byte of data from the IO memory address space
+ */
+
+ Bit32u BX_CPP_AttrRegparmN(2)
+bx_devices_c::inp(Bit16u addr, unsigned io_len)
+{
+ Bit8u handle;
+ Bit32u ret;
+
+ BX_INSTR_INP(addr, io_len);
+
+ handle = read_handler_id[addr];
+ if ((io_read_handler[handle].funct != NULL) &&
+ (io_read_handler[handle].mask & io_len)) {
+ ret = (* io_read_handler[handle].funct)(io_read_handler[handle].this_ptr,
+ (Bit32u) addr, io_len);
+ } else {
+ switch (io_len) {
+ case 1: ret = 0xff; break;
+ case 2: ret = 0xffff; break;
+ default: ret = 0xffffffff; break;
+ }
+ BX_ERROR(("read from port 0x%04x with len %d returns 0x%x", addr, io_len, ret));
+ }
+ BX_INSTR_INP2(addr, io_len, ret);
+ BX_DBG_IO_REPORT(addr, io_len, BX_READ, ret);
+
+ return(ret);
+}
+
+
+/*
+ * Write a byte of data to the IO memory address space.
+ */
+
+ void BX_CPP_AttrRegparmN(3)
+bx_devices_c::outp(Bit16u addr, Bit32u value, unsigned io_len)
+{
+ Bit8u handle;
+
+ BX_INSTR_OUTP(addr, io_len);
+ BX_INSTR_OUTP2(addr, io_len, value);
+
+ BX_DBG_IO_REPORT(addr, io_len, BX_WRITE, value);
+ handle = write_handler_id[addr];
+ if ((io_write_handler[handle].funct != NULL) &&
+ (io_write_handler[handle].mask & io_len)) {
+ (* io_write_handler[handle].funct)(io_write_handler[handle].this_ptr,
+ (Bit32u) addr, value, io_len);
+ } else {
+ BX_ERROR(("write to port 0x%04x with len %d ignored", addr, io_len));
+ }
+}
+
+bx_bool bx_devices_c::is_serial_enabled ()
+{
+ for (int i=0; i<BX_N_SERIAL_PORTS; i++) {
+ if (SIM->get_param_bool (BXP_COMx_ENABLED(i+1))->get())
+ return true;
+ }
+ return false;
+}
+
+bx_bool bx_devices_c::is_usb_enabled ()
+{
+ for (int i=0; i<BX_N_USB_HUBS; i++) {
+ if (SIM->get_param_bool (BXP_USBx_ENABLED(i+1))->get())
+ return true;
+ }
+ return false;
+}
+
+bx_bool bx_devices_c::is_parallel_enabled ()
+{
+ for (int i=0; i<BX_N_PARALLEL_PORTS; i++) {
+ if (SIM->get_param_bool (BXP_PARPORTx_ENABLED(i+1))->get())
+ return true;
+ }
+ return false;
+}
diff --git a/tools/ioemu/iodev/dma.cc b/tools/ioemu/iodev/dma.cc
new file mode 100644
index 0000000000..afe0238a92
--- /dev/null
+++ b/tools/ioemu/iodev/dma.cc
@@ -0,0 +1,825 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: dma.cc,v 1.30 2003/07/31 15:29:34 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS theDmaDevice->
+
+#define DMA_MODE_DEMAND 0
+#define DMA_MODE_SINGLE 1
+#define DMA_MODE_BLOCK 2
+#define DMA_MODE_CASCADE 3
+
+bx_dma_c *theDmaDevice = NULL;
+
+ int
+libdma_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theDmaDevice = new bx_dma_c ();
+ bx_devices.pluginDmaDevice = theDmaDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theDmaDevice, BX_PLUGIN_DMA);
+ return(0); // Success
+}
+
+ void
+libdma_LTX_plugin_fini(void)
+{
+}
+
+bx_dma_c::bx_dma_c(void)
+{
+ put("DMA");
+ settype(DMALOG);
+}
+
+bx_dma_c::~bx_dma_c(void)
+{
+ BX_DEBUG(("Exit."));
+}
+
+ unsigned
+bx_dma_c::registerDMA8Channel(
+ unsigned channel,
+ void (* dmaRead)(Bit8u *data_byte),
+ void (* dmaWrite)(Bit8u *data_byte),
+ const char *name
+ )
+{
+ if (channel > 3) {
+ BX_PANIC(("registerDMA8Channel: invalid channel number(%u).", channel));
+ return 0; // Fail.
+ }
+ if (BX_DMA_THIS s[0].chan[channel].used) {
+ BX_PANIC(("registerDMA8Channel: channel(%u) already in use.", channel));
+ return 0; // Fail.
+ }
+ BX_INFO(("channel %u used by %s", channel, name));
+ BX_DMA_THIS h[channel].dmaRead8 = dmaRead;
+ BX_DMA_THIS h[channel].dmaWrite8 = dmaWrite;
+ BX_DMA_THIS s[0].chan[channel].used = 1;
+ return 1; // OK.
+}
+
+ unsigned
+bx_dma_c::registerDMA16Channel(
+ unsigned channel,
+ void (* dmaRead)(Bit16u *data_word),
+ void (* dmaWrite)(Bit16u *data_word),
+ const char *name
+ )
+{
+ if ((channel < 4) || (channel > 7)) {
+ BX_PANIC(("registerDMA16Channel: invalid channel number(%u).", channel));
+ return 0; // Fail.
+ }
+ if (BX_DMA_THIS s[1].chan[channel & 0x03].used) {
+ BX_PANIC(("registerDMA16Channel: channel(%u) already in use.", channel));
+ return 0; // Fail.
+ }
+ BX_INFO(("channel %u used by %s", channel, name));
+ channel &= 0x03;
+ BX_DMA_THIS h[channel].dmaRead16 = dmaRead;
+ BX_DMA_THIS h[channel].dmaWrite16 = dmaWrite;
+ BX_DMA_THIS s[1].chan[channel].used = 1;
+ return 1; // OK.
+}
+
+ unsigned
+bx_dma_c::unregisterDMAChannel(unsigned channel)
+{
+ bx_bool ma_sl = (channel > 3);
+ BX_DMA_THIS s[ma_sl].chan[channel & 0x03].used = 0;
+ BX_INFO(("channel %u no longer used", channel));
+ return 1;
+}
+
+ unsigned
+bx_dma_c::get_TC(void)
+{
+ return BX_DMA_THIS TC;
+}
+
+
+ void
+bx_dma_c::init(void)
+{
+ unsigned c, i, j;
+ BX_DEBUG(("Init $Id: dma.cc,v 1.30 2003/07/31 15:29:34 vruppert Exp $"));
+
+ /* 8237 DMA controller */
+
+ for (i=0; i < 2; i++) {
+ for (j=0; j < 4; j++) {
+ BX_DMA_THIS s[i].DRQ[j] = 0;
+ BX_DMA_THIS s[i].DACK[j] = 0;
+ }
+ }
+ BX_DMA_THIS HLDA = 0;
+ BX_DMA_THIS TC = 0;
+
+ // 0000..000F
+ for (i=0x0000; i<=0x000F; i++) {
+ DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
+ DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
+ }
+
+ // 00081..008F
+ for (i=0x0081; i<=0x008F; i++) {
+ DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
+ DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
+ }
+
+ // 000C0..00DE
+ for (i=0x00C0; i<=0x00DE; i+=2) {
+ DEV_register_ioread_handler(this, read_handler, i, "DMA controller", 1);
+ DEV_register_iowrite_handler(this, write_handler, i, "DMA controller", 3);
+ }
+
+
+ for (i=0; i<2; i++) {
+ for (c=0; c<4; c++) {
+ BX_DMA_THIS s[i].chan[c].mode.mode_type = 0; // demand mode
+ BX_DMA_THIS s[i].chan[c].mode.address_decrement = 0; // address increment
+ BX_DMA_THIS s[i].chan[c].mode.autoinit_enable = 0; // autoinit disable
+ BX_DMA_THIS s[i].chan[c].mode.transfer_type = 0; // verify
+ BX_DMA_THIS s[i].chan[c].base_address = 0;
+ BX_DMA_THIS s[i].chan[c].current_address = 0;
+ BX_DMA_THIS s[i].chan[c].base_count = 0;
+ BX_DMA_THIS s[i].chan[c].current_count = 0;
+ BX_DMA_THIS s[i].chan[c].page_reg = 0;
+ BX_DMA_THIS s[i].chan[c].used = 0;
+ }
+ }
+ BX_DMA_THIS s[1].chan[0].used = 1; // cascade channel in use
+ BX_INFO(("channel 4 used by cascade"));
+ bios_init();
+}
+
+/* Remove it when guest fw ready*/
+ void
+bx_dma_c::bios_init(void){
+ BX_DMA_THIS s[1].mask[0] = 0; // unmask cascade channel
+ BX_DMA_THIS s[1].chan[0].mode.mode_type = 3; // cascade mode for channel 4
+}
+
+ void
+bx_dma_c::reset(unsigned type)
+{
+ reset_controller(0);
+ reset_controller(1);
+ bios_init();
+}
+
+ void
+bx_dma_c::reset_controller(unsigned num)
+{
+ BX_DMA_THIS s[num].mask[0] = 1;
+ BX_DMA_THIS s[num].mask[1] = 1;
+ BX_DMA_THIS s[num].mask[2] = 1;
+ BX_DMA_THIS s[num].mask[3] = 1;
+ BX_DMA_THIS s[num].command_reg = 0;
+ BX_DMA_THIS s[num].status_reg = 0;
+ BX_DMA_THIS s[num].request_reg = 0;
+ BX_DMA_THIS s[num].temporary_reg = 0;
+ BX_DMA_THIS s[num].flip_flop = 0;
+}
+
+ // index to find channel from register number (only [0],[1],[2],[6] used)
+ Bit8u channelindex[7] = {2, 3, 1, 0, 0, 0, 0};
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_dma_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_DMA_SMF
+ bx_dma_c *class_ptr = (bx_dma_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+ /* 8237 DMA controller */
+ Bit32u BX_CPP_AttrRegparmN(2)
+bx_dma_c::read( Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_DMA_SMF
+
+ Bit8u retval;
+ Bit8u channel;
+ bx_bool ma_sl;
+
+ BX_DEBUG(("read addr=%04x", (unsigned) address));
+
+#if BX_DMA_FLOPPY_IO < 1
+ /* if we're not supporting DMA/floppy IO just return a bogus value */
+ return(0xff);
+#endif
+
+ switch (address) {
+ case 0x00: /* DMA-1 current address, channel 0 */
+ case 0x02: /* DMA-1 current address, channel 1 */
+ case 0x04: /* DMA-1 current address, channel 2 */
+ case 0x06: /* DMA-1 current address, channel 3 */
+ case 0xc0: /* DMA-2 current address, channel 0 */
+ case 0xc4: /* DMA-2 current address, channel 1 */
+ case 0xc8: /* DMA-2 current address, channel 2 */
+ case 0xcc: /* DMA-2 current address, channel 3 */
+ ma_sl = (address >= 0xc0);
+ channel = (address >> (1 + ma_sl)) & 0x03;
+ if (BX_DMA_THIS s[ma_sl].flip_flop==0) {
+ BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
+ return(BX_DMA_THIS s[ma_sl].chan[channel].current_address & 0xff);
+ }
+ else {
+ BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
+ return(BX_DMA_THIS s[ma_sl].chan[channel].current_address >> 8);
+ }
+
+ case 0x01: /* DMA-1 current count, channel 0 */
+ case 0x03: /* DMA-1 current count, channel 1 */
+ case 0x05: /* DMA-1 current count, channel 2 */
+ case 0x07: /* DMA-1 current count, channel 3 */
+ case 0xc2: /* DMA-2 current count, channel 0 */
+ case 0xc6: /* DMA-2 current count, channel 1 */
+ case 0xca: /* DMA-2 current count, channel 2 */
+ case 0xce: /* DMA-2 current count, channel 3 */
+ ma_sl = (address >= 0xc2);
+ channel = (address >> (1 + ma_sl)) & 0x03;
+ if (BX_DMA_THIS s[ma_sl].flip_flop==0) {
+ BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
+ return(BX_DMA_THIS s[ma_sl].chan[channel].current_count & 0xff);
+ }
+ else {
+ BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
+ return(BX_DMA_THIS s[ma_sl].chan[channel].current_count >> 8);
+ }
+
+ case 0x08: // DMA-1 Status Register
+ case 0xd0: // DMA-2 Status Register
+ // bit 7: 1 = channel 3 request
+ // bit 6: 1 = channel 2 request
+ // bit 5: 1 = channel 1 request
+ // bit 4: 1 = channel 0 request
+ // bit 3: 1 = channel 3 has reached terminal count
+ // bit 2: 1 = channel 2 has reached terminal count
+ // bit 1: 1 = channel 1 has reached terminal count
+ // bit 0: 1 = channel 0 has reached terminal count
+ // reading this register clears lower 4 bits (hold flags)
+ ma_sl = (address == 0xd0);
+ retval = BX_DMA_THIS s[ma_sl].status_reg;
+ BX_DMA_THIS s[ma_sl].status_reg &= 0xf0;
+ return(retval);
+ break;
+ case 0x0d: // DMA-1: temporary register
+ case 0xda: // DMA-2: temporary register
+ ma_sl = (address == 0xda);
+ BX_ERROR(("DMA-%d: read of temporary register", ma_sl+1));
+ // Note: write to 0x0D clears temporary register
+ return(0);
+ break;
+
+ case 0x0081: // DMA-1 page register, channel 2
+ case 0x0082: // DMA-1 page register, channel 3
+ case 0x0083: // DMA-1 page register, channel 1
+ case 0x0087: // DMA-1 page register, channel 0
+ channel = channelindex[address - 0x81];
+ return( BX_DMA_THIS s[0].chan[channel].page_reg );
+
+ case 0x0089: // DMA-2 page register, channel 2
+ case 0x008a: // DMA-2 page register, channel 3
+ case 0x008b: // DMA-2 page register, channel 1
+ case 0x008f: // DMA-2 page register, channel 0
+ channel = channelindex[address - 0x89];
+ return( BX_DMA_THIS s[1].chan[channel].page_reg );
+
+ case 0x0084:
+ case 0x0085:
+ case 0x0086:
+ case 0x0088:
+ case 0x008c:
+ case 0x008d:
+ case 0x008e:
+ BX_DEBUG(("read: extra page register 0x%04x unsupported", (unsigned) address));
+ return(0);
+
+ default:
+ BX_ERROR(("read: unsupported address=%04x", (unsigned) address));
+ return(0);
+ }
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_dma_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_DMA_SMF
+ bx_dma_c *class_ptr = (bx_dma_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+
+ /* 8237 DMA controller */
+ void BX_CPP_AttrRegparmN(3)
+bx_dma_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_DMA_SMF
+ Bit8u set_mask_bit;
+ Bit8u channel;
+ bx_bool ma_sl;
+
+ if (io_len > 1) {
+ if ( (io_len == 2) && (address == 0x0b) ) {
+#if BX_USE_DMA_SMF
+ BX_DMA_THIS write_handler(NULL, address, value & 0xff, 1);
+ BX_DMA_THIS write_handler(NULL, address+1, value >> 8, 1);
+#else
+ BX_DMA_THIS write(address, value & 0xff, 1);
+ BX_DMA_THIS write(address+1, value >> 8, 1);
+#endif
+ return;
+ }
+
+ BX_ERROR(("io write to address %08x, len=%u",
+ (unsigned) address, (unsigned) io_len));
+ return;
+ }
+
+ BX_DEBUG(("write: address=%04x value=%02x",
+ (unsigned) address, (unsigned) value));
+
+#if BX_DMA_FLOPPY_IO < 1
+ /* if we're not supporting DMA/floppy IO just return */
+ return;
+#endif
+
+ switch (address) {
+ case 0x00:
+ case 0x02:
+ case 0x04:
+ case 0x06:
+ case 0xc0:
+ case 0xc4:
+ case 0xc8:
+ case 0xcc:
+ ma_sl = (address >= 0xc0);
+ channel = (address >> (1 + ma_sl)) & 0x03;
+ BX_DEBUG((" DMA-%d base and current address, channel %d", ma_sl+1, channel));
+ if (BX_DMA_THIS s[ma_sl].flip_flop==0) { /* 1st byte */
+ BX_DMA_THIS s[ma_sl].chan[channel].base_address = value;
+ BX_DMA_THIS s[ma_sl].chan[channel].current_address = value;
+ }
+ else { /* 2nd byte */
+ BX_DMA_THIS s[ma_sl].chan[channel].base_address |= (value << 8);
+ BX_DMA_THIS s[ma_sl].chan[channel].current_address |= (value << 8);
+ BX_DEBUG((" base = %04x",
+ (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_address));
+ BX_DEBUG((" curr = %04x",
+ (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].current_address));
+ }
+ BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
+ return;
+ break;
+
+ case 0x01:
+ case 0x03:
+ case 0x05:
+ case 0x07:
+ case 0xc2:
+ case 0xc6:
+ case 0xca:
+ case 0xce:
+ ma_sl = (address >= 0xc2);
+ channel = (address >> (1 + ma_sl)) & 0x03;
+ BX_DEBUG((" DMA-%d base and current count, channel %d", ma_sl+1, channel));
+ if (BX_DMA_THIS s[ma_sl].flip_flop==0) { /* 1st byte */
+ BX_DMA_THIS s[ma_sl].chan[channel].base_count = value;
+ BX_DMA_THIS s[ma_sl].chan[channel].current_count = value;
+ }
+ else { /* 2nd byte */
+ BX_DMA_THIS s[ma_sl].chan[channel].base_count |= (value << 8);
+ BX_DMA_THIS s[ma_sl].chan[channel].current_count |= (value << 8);
+ BX_DEBUG((" base = %04x",
+ (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_count));
+ BX_DEBUG((" curr = %04x",
+ (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].current_count));
+ }
+ BX_DMA_THIS s[ma_sl].flip_flop = !BX_DMA_THIS s[ma_sl].flip_flop;
+ return;
+ break;
+
+ case 0x08: /* DMA-1: command register */
+ case 0xd0: /* DMA-2: command register */
+ ma_sl = (address == 0xd0);
+ if (value != 0x00)
+ BX_ERROR(("write to command register: value(%02xh) not 0x00",
+ (unsigned) value));
+ BX_DMA_THIS s[ma_sl].command_reg = value;
+ return;
+ break;
+
+ case 0x09: // DMA-1: request register
+ case 0xd2: // DMA-2: request register
+ ma_sl = (address == 0xd2);
+ channel = value & 0x03;
+ BX_ERROR(("DMA-%d: write to request register (%02x)", ma_sl+1, (unsigned) value));
+ // note: write to 0x0d clears this register
+ if (value & 0x04) {
+ // set request bit
+ BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4));
+ BX_DEBUG(("DMA-%d: set request bit for channel %u", ma_sl+1, (unsigned) channel));
+ }
+ else {
+ // clear request bit
+ BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4));
+ BX_DEBUG(("DMA-%d: cleared request bit for channel %u", ma_sl+1, (unsigned) channel));
+ }
+ control_HRQ(ma_sl);
+ return;
+ break;
+
+ case 0x0a:
+ case 0xd4:
+ ma_sl = (address == 0xd4);
+ set_mask_bit = value & 0x04;
+ channel = value & 0x03;
+ BX_DMA_THIS s[ma_sl].mask[channel] = (set_mask_bit > 0);
+ BX_DEBUG(("DMA-%d: set_mask_bit=%u, channel=%u, mask now=%02xh", ma_sl+1,
+ (unsigned) set_mask_bit, (unsigned) channel, (unsigned) BX_DMA_THIS s[ma_sl].mask[channel]));
+ control_HRQ(ma_sl);
+ return;
+ break;
+
+ case 0x0b: /* DMA-1 mode register */
+ case 0xd6: /* DMA-2 mode register */
+ ma_sl = (address == 0xd6);
+ channel = value & 0x03;
+ BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type = (value >> 6) & 0x03;
+ BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement = (value >> 5) & 0x01;
+ BX_DMA_THIS s[ma_sl].chan[channel].mode.autoinit_enable = (value >> 4) & 0x01;
+ BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type = (value >> 2) & 0x03;
+ BX_DEBUG(("DMA-%d: mode register[%u] = %02x", ma_sl+1,
+ (unsigned) channel, (unsigned) value));
+ return;
+ break;
+
+ case 0x0c: /* DMA-1 clear byte flip/flop */
+ case 0xd8: /* DMA-2 clear byte flip/flop */
+ ma_sl = (address == 0xd8);
+ BX_DEBUG(("DMA-%d: clear flip/flop", ma_sl+1));
+ BX_DMA_THIS s[ma_sl].flip_flop = 0;
+ return;
+ break;
+
+ case 0x0d: // DMA-1: master clear
+ case 0xda: // DMA-2: master clear
+ ma_sl = (address == 0xda);
+ BX_DEBUG(("DMA-%d: master clear", ma_sl+1));
+ // writing any value to this port resets DMA controller 1 / 2
+ // same action as a hardware reset
+ // mask register is set (chan 0..3 disabled)
+ // command, status, request, temporary, and byte flip-flop are all cleared
+ reset_controller(ma_sl);
+ return;
+ break;
+
+ case 0x0e: // DMA-1: clear mask register
+ case 0xdc: // DMA-2: clear mask register
+ ma_sl = (address == 0xdc);
+ BX_DEBUG(("DMA-%d: clear mask register", ma_sl+1));
+ BX_DMA_THIS s[ma_sl].mask[0] = 0;
+ BX_DMA_THIS s[ma_sl].mask[1] = 0;
+ BX_DMA_THIS s[ma_sl].mask[2] = 0;
+ BX_DMA_THIS s[ma_sl].mask[3] = 0;
+ control_HRQ(ma_sl);
+ return;
+ break;
+
+ case 0x0f: // DMA-1: write all mask bits
+ case 0xde: // DMA-2: write all mask bits
+ ma_sl = (address == 0xde);
+ BX_DEBUG(("DMA-%d: write all mask bits", ma_sl+1));
+ BX_DMA_THIS s[ma_sl].mask[0] = value & 0x01; value >>= 1;
+ BX_DMA_THIS s[ma_sl].mask[1] = value & 0x01; value >>= 1;
+ BX_DMA_THIS s[ma_sl].mask[2] = value & 0x01; value >>= 1;
+ BX_DMA_THIS s[ma_sl].mask[3] = value & 0x01;
+ control_HRQ(ma_sl);
+ return;
+ break;
+
+ case 0x81: /* DMA-1 page register, channel 2 */
+ case 0x82: /* DMA-1 page register, channel 3 */
+ case 0x83: /* DMA-1 page register, channel 1 */
+ case 0x87: /* DMA-1 page register, channel 0 */
+ /* address bits A16-A23 for DMA channel */
+ channel = channelindex[address - 0x81];
+ BX_DMA_THIS s[0].chan[channel].page_reg = value;
+ BX_DEBUG(("DMA-1: page register %d = %02x", channel, (unsigned) value));
+ return;
+ break;
+
+ case 0x89: /* DMA-2 page register, channel 2 */
+ case 0x8a: /* DMA-2 page register, channel 3 */
+ case 0x8b: /* DMA-2 page register, channel 1 */
+ case 0x8f: /* DMA-2 page register, channel 0 */
+ /* address bits A16-A23 for DMA channel */
+ channel = channelindex[address - 0x89];
+ BX_DMA_THIS s[1].chan[channel].page_reg = value;
+ BX_DEBUG(("DMA-2: page register %d = %02x", channel + 4, (unsigned) value));
+ return;
+ break;
+
+ case 0x0084:
+ case 0x0085:
+ case 0x0086:
+ case 0x0088:
+ case 0x008c:
+ case 0x008d:
+ case 0x008e:
+ BX_DEBUG(("write: extra page register 0x%04x unsupported", (unsigned) address));
+ return;
+ break;
+
+ default:
+ BX_ERROR(("write ignored: %04xh = %02xh",
+ (unsigned) address, (unsigned) value));
+ }
+}
+
+ void
+bx_dma_c::set_DRQ(unsigned channel, bx_bool val)
+{
+ Bit32u dma_base, dma_roof;
+ bx_bool ma_sl;
+
+ if (channel > 7) {
+ BX_PANIC(("set_DRQ() channel > 7"));
+ return;
+ }
+ ma_sl = (channel > 3);
+ BX_DMA_THIS s[ma_sl].DRQ[channel & 0x03] = val;
+ if (!BX_DMA_THIS s[ma_sl].chan[channel & 0x03].used) {
+ BX_PANIC(("set_DRQ(): channel %d not connected to device", channel));
+ return;
+ }
+ channel &= 0x03;
+ if (!val) {
+ //BX_DEBUG(("bx_dma_c::DRQ(): val == 0"));
+ // clear bit in status reg
+ BX_DMA_THIS s[ma_sl].status_reg &= ~(1 << (channel+4));
+
+ control_HRQ(ma_sl);
+ return;
+ }
+
+#if 0
+ BX_INFO(("mask[%d]: %02x", channel, (unsigned) BX_DMA_THIS s[0].mask[channel]));
+ BX_INFO(("flip_flop: %u", (unsigned) BX_DMA_THIS s[0].flip_flop));
+ BX_INFO(("status_reg: %02x", (unsigned) BX_DMA_THIS s[0].status_reg));
+ BX_INFO(("mode_type: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.mode_type));
+ BX_INFO(("address_decrement: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.address_decrement));
+ BX_INFO(("autoinit_enable: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.autoinit_enable));
+ BX_INFO(("transfer_type: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].mode.transfer_type));
+ BX_INFO(("base_address: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].base_address));
+ BX_INFO(("current_address: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].current_address));
+ BX_INFO(("base_count: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].base_count));
+ BX_INFO(("current_count: %04x", (unsigned) BX_DMA_THIS s[0].chan[channel].current_count));
+ BX_INFO(("page_reg: %02x", (unsigned) BX_DMA_THIS s[0].chan[channel].page_reg));
+#endif
+
+ BX_DMA_THIS s[ma_sl].status_reg |= (1 << (channel+4));
+
+ if ( (BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_SINGLE) &&
+ (BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_DEMAND) &&
+ (BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type != DMA_MODE_CASCADE) )
+ BX_PANIC(("set_DRQ: mode_type(%02x) not handled",
+ (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].mode.mode_type));
+
+ dma_base = (BX_DMA_THIS s[ma_sl].chan[channel].page_reg << 16) |
+ (BX_DMA_THIS s[ma_sl].chan[channel].base_address << ma_sl);
+ if (BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement==0) {
+ dma_roof = dma_base + (BX_DMA_THIS s[ma_sl].chan[channel].base_count << ma_sl);
+ } else {
+ dma_roof = dma_base - (BX_DMA_THIS s[ma_sl].chan[channel].base_count << ma_sl);
+ }
+ if ( (dma_base & (0x7fff0000 << ma_sl)) != (dma_roof & (0x7fff0000 << ma_sl)) ) {
+ BX_INFO(("dma_base = %08x", (unsigned) dma_base));
+ BX_INFO(("dma_base_count = %08x", (unsigned) BX_DMA_THIS s[ma_sl].chan[channel].base_count));
+ BX_INFO(("dma_roof = %08x", (unsigned) dma_roof));
+ BX_PANIC(("request outside %dk boundary", 64 << ma_sl));
+ }
+
+ control_HRQ(ma_sl);
+}
+
+ void
+bx_dma_c::control_HRQ(bx_bool ma_sl)
+{
+ unsigned channel;
+
+ // deassert HRQ if no DRQ is pending
+ if ((BX_DMA_THIS s[ma_sl].status_reg & 0xf0) == 0) {
+ if (ma_sl) {
+ bx_pc_system.set_HRQ(0);
+ } else {
+ BX_DMA_THIS set_DRQ(4, 0);
+ }
+ return;
+ }
+ // find highest priority channel
+ for (channel=0; channel<4; channel++) {
+ if ( (BX_DMA_THIS s[ma_sl].status_reg & (1 << (channel+4))) &&
+ (BX_DMA_THIS s[ma_sl].mask[channel]==0) ) {
+ if (ma_sl) {
+ // assert Hold ReQuest line to CPU
+ bx_pc_system.set_HRQ(1);
+ } else {
+ // send DRQ to cascade channel of the master
+ BX_DMA_THIS set_DRQ(4, 1);
+ }
+ break;
+ }
+ }
+}
+
+ void
+bx_dma_c::raise_HLDA(void)
+{
+ unsigned channel;
+ Bit32u phy_addr;
+ bx_bool count_expired = 0;
+ bx_bool ma_sl = 0;
+
+ BX_DMA_THIS HLDA = 1;
+ // find highest priority channel
+ for (channel=0; channel<4; channel++) {
+ if ( (BX_DMA_THIS s[1].status_reg & (1 << (channel+4))) &&
+ (BX_DMA_THIS s[1].mask[channel]==0) ) {
+ ma_sl = 1;
+ break;
+ }
+ }
+ if (channel == 0) { // master cascade channel
+ BX_DMA_THIS s[1].DACK[0] = 1;
+ for (channel=0; channel<4; channel++) {
+ if ( (BX_DMA_THIS s[0].status_reg & (1 << (channel+4))) &&
+ (BX_DMA_THIS s[0].mask[channel]==0) ) {
+ ma_sl = 0;
+ break;
+ }
+ }
+ }
+ if (channel >= 4) {
+ // wait till they're unmasked
+ return;
+ }
+
+ //BX_DEBUG(("hlda: OK in response to DRQ(%u)", (unsigned) channel));
+ phy_addr = (BX_DMA_THIS s[ma_sl].chan[channel].page_reg << 16) |
+ (BX_DMA_THIS s[ma_sl].chan[channel].current_address << ma_sl);
+
+ BX_DMA_THIS s[ma_sl].DACK[channel] = 1;
+ // check for expiration of count, so we can signal TC and DACK(n)
+ // at the same time.
+ if (BX_DMA_THIS s[ma_sl].chan[channel].mode.address_decrement==0)
+ BX_DMA_THIS s[ma_sl].chan[channel].current_address++;
+ else
+ BX_DMA_THIS s[ma_sl].chan[channel].current_address--;
+ BX_DMA_THIS s[ma_sl].chan[channel].current_count--;
+ if (BX_DMA_THIS s[ma_sl].chan[channel].current_count == 0xffff) {
+ // count expired, done with transfer
+ // assert TC, deassert HRQ & DACK(n) lines
+ BX_DMA_THIS s[ma_sl].status_reg |= (1 << channel); // hold TC in status reg
+ BX_DMA_THIS TC = 1;
+ count_expired = 1;
+ if (BX_DMA_THIS s[ma_sl].chan[channel].mode.autoinit_enable == 0) {
+ // set mask bit if not in autoinit mode
+ BX_DMA_THIS s[ma_sl].mask[channel] = 1;
+ }
+ else {
+ // count expired, but in autoinit mode
+ // reload count and base address
+ BX_DMA_THIS s[ma_sl].chan[channel].current_address =
+ BX_DMA_THIS s[ma_sl].chan[channel].base_address;
+ BX_DMA_THIS s[ma_sl].chan[channel].current_count =
+ BX_DMA_THIS s[ma_sl].chan[channel].base_count;
+ }
+ }
+
+ Bit8u data_byte;
+ Bit16u data_word;
+
+ if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 1) { // write
+ // DMA controlled xfer of byte from I/O to Memory
+
+ if (!ma_sl) {
+ if (BX_DMA_THIS h[channel].dmaWrite8)
+ BX_DMA_THIS h[channel].dmaWrite8(&data_byte);
+ else
+ BX_PANIC(("no dmaWrite handler for channel %u.", channel));
+
+ BX_MEM_WRITE_PHYSICAL(phy_addr, 1, &data_byte);
+
+ BX_DBG_DMA_REPORT(phy_addr, 1, BX_WRITE, data_byte);
+ }
+ else {
+ if (BX_DMA_THIS h[channel].dmaWrite16)
+ BX_DMA_THIS h[channel].dmaWrite16(&data_word);
+ else
+ BX_PANIC(("no dmaWrite handler for channel %u.", channel));
+
+ BX_MEM_WRITE_PHYSICAL(phy_addr, 2, &data_word);
+
+ BX_DBG_DMA_REPORT(phy_addr, 2, BX_WRITE, data_word);
+ }
+ }
+ else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 2) { // read
+ // DMA controlled xfer of byte from Memory to I/O
+
+ if (!ma_sl) {
+ BX_MEM_READ_PHYSICAL(phy_addr, 1, &data_byte);
+
+ if (BX_DMA_THIS h[channel].dmaRead8)
+ BX_DMA_THIS h[channel].dmaRead8(&data_byte);
+
+ BX_DBG_DMA_REPORT(phy_addr, 1, BX_READ, data_byte);
+ }
+ else {
+ BX_MEM_READ_PHYSICAL(phy_addr, 2, &data_word);
+
+ if (BX_DMA_THIS h[channel].dmaRead16)
+ BX_DMA_THIS h[channel].dmaRead16(&data_word);
+
+ BX_DBG_DMA_REPORT(phy_addr, 2, BX_READ, data_word);
+ }
+ }
+ else if (BX_DMA_THIS s[ma_sl].chan[channel].mode.transfer_type == 0) {
+ // verify
+
+ if (!ma_sl) {
+ if (BX_DMA_THIS h[channel].dmaWrite8)
+ BX_DMA_THIS h[channel].dmaWrite8(&data_byte);
+ else
+ BX_PANIC(("no dmaWrite handler for channel %u.", channel));
+ }
+ else {
+ if (BX_DMA_THIS h[channel].dmaWrite16)
+ BX_DMA_THIS h[channel].dmaWrite16(&data_word);
+ else
+ BX_PANIC(("no dmaWrite handler for channel %u.", channel));
+ }
+ }
+ else {
+ BX_PANIC(("hlda: transfer_type 3 is undefined"));
+ }
+
+ if (count_expired) {
+ BX_DMA_THIS TC = 0; // clear TC, adapter card already notified
+ BX_DMA_THIS HLDA = 0;
+ bx_pc_system.set_HRQ(0); // clear HRQ to CPU
+ BX_DMA_THIS s[ma_sl].DACK[channel] = 0; // clear DACK to adapter card
+ if (!ma_sl) {
+ BX_DMA_THIS set_DRQ(4, 0); // clear DRQ to cascade
+ BX_DMA_THIS s[1].DACK[0] = 0; // clear DACK to cascade
+ }
+ }
+}
diff --git a/tools/ioemu/iodev/dma.h b/tools/ioemu/iodev/dma.h
new file mode 100644
index 0000000000..9f6c4eb60e
--- /dev/null
+++ b/tools/ioemu/iodev/dma.h
@@ -0,0 +1,114 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: dma.h,v 1.15 2003/05/03 07:41:27 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#ifndef _PCDMA_H
+#define _PCDMA_H
+
+
+#if BX_USE_DMA_SMF
+# define BX_DMA_SMF static
+# define BX_DMA_THIS theDmaDevice->
+#else
+# define BX_DMA_SMF
+# define BX_DMA_THIS this->
+#endif
+
+
+
+class bx_dma_c : public bx_dma_stub_c {
+public:
+
+ bx_dma_c();
+ ~bx_dma_c(void);
+
+ virtual void init(void);
+ virtual void bios_init(void);
+ virtual void reset(unsigned type);
+ virtual void raise_HLDA(void);
+ virtual void set_DRQ(unsigned channel, bx_bool val);
+ virtual unsigned get_TC(void);
+
+ virtual unsigned registerDMA8Channel(unsigned channel,
+ void (* dmaRead)(Bit8u *data_byte),
+ void (* dmaWrite)(Bit8u *data_byte),
+ const char *name);
+ virtual unsigned registerDMA16Channel(unsigned channel,
+ void (* dmaRead)(Bit16u *data_word),
+ void (* dmaWrite)(Bit16u *data_word),
+ const char *name);
+ virtual unsigned unregisterDMAChannel(unsigned channel);
+
+private:
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_DMA_SMF
+ Bit32u read( Bit32u address, unsigned io_len) BX_CPP_AttrRegparmN(2);
+ void write(Bit32u address, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3);
+#endif
+ BX_DMA_SMF void control_HRQ(bx_bool ma_sl);
+ BX_DMA_SMF void reset_controller(unsigned num);
+
+ struct {
+ bx_bool DRQ[4]; // DMA Request
+ bx_bool DACK[4]; // DMA Acknowlege
+
+ bx_bool mask[4];
+ bx_bool flip_flop;
+ Bit8u status_reg;
+ Bit8u command_reg;
+ Bit8u request_reg;
+ Bit8u temporary_reg;
+ struct {
+ struct {
+ Bit8u mode_type;
+ Bit8u address_decrement;
+ Bit8u autoinit_enable;
+ Bit8u transfer_type;
+ } mode;
+ Bit16u base_address;
+ Bit16u current_address;
+ Bit16u base_count;
+ Bit16u current_count;
+ Bit8u page_reg;
+ bx_bool used;
+ } chan[4]; /* DMA channels 0..3 */
+ } s[2]; // state information DMA-1 / DMA-2
+
+ bx_bool HLDA; // Hold Acknowlege
+ bx_bool TC; // Terminal Count
+
+ struct {
+ void (* dmaRead8)(Bit8u *data_byte);
+ void (* dmaWrite8)(Bit8u *data_byte);
+ void (* dmaRead16)(Bit16u *data_word);
+ void (* dmaWrite16)(Bit16u *data_word);
+ } h[4]; // DMA read and write handlers
+
+ };
+
+#endif // #ifndef _PCDMA_H
diff --git a/tools/ioemu/iodev/eth.cc b/tools/ioemu/iodev/eth.cc
new file mode 100644
index 0000000000..d6ee9d2948
--- /dev/null
+++ b/tools/ioemu/iodev/eth.cc
@@ -0,0 +1,194 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth.cc,v 1.16 2003/04/28 13:01:09 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// eth.cc - helper code to find and create pktmover classes
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT
+
+#define LOG_THIS /* not needed */
+
+eth_locator_c *eth_locator_c::all;
+
+//
+// Each pktmover module has a static locator class that registers
+// here
+//
+eth_locator_c::eth_locator_c(const char *type)
+{
+ next = all;
+ all = this;
+ this->type = type;
+}
+
+#ifdef ETH_NULL
+extern class bx_null_locator_c bx_null_match;
+#endif
+#ifdef ETH_FBSD
+extern class bx_fbsd_locator_c bx_fbsd_match;
+#endif
+#ifdef ETH_LINUX
+extern class bx_linux_locator_c bx_linux_match;
+#endif
+#ifdef ETH_WIN32
+extern class bx_win32_locator_c bx_win32_match;
+#endif
+#if HAVE_ETHERTAP
+extern class bx_tap_locator_c bx_tap_match;
+#endif
+#if HAVE_TUNTAP
+extern class bx_tuntap_locator_c bx_tuntap_match;
+#endif
+#ifdef ETH_TEST
+extern bx_test_match;
+#endif
+#ifdef ETH_ARPBACK
+extern class bx_arpback_locator_c bx_arpback_match;
+#endif
+
+//
+// Called by ethernet chip emulations to locate and create a pktmover
+// object
+//
+eth_pktmover_c *
+eth_locator_c::create(const char *type, const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh, void *rxarg)
+{
+#ifdef eth_static_constructors
+ for (eth_locator_c *p = all; p != NULL; p = p->next) {
+ if (strcmp(type, p->type) == 0)
+ return (p->allocate(netif, macaddr, rxh, rxarg));
+ }
+#else
+ eth_locator_c *ptr = 0;
+
+#ifdef ETH_ARPBACK
+ {
+ if (!strcmp(type, "arpback"))
+ ptr = (eth_locator_c *) &bx_arpback_match;
+ }
+#endif
+#ifdef ETH_NULL
+ {
+ if (!strcmp(type, "null"))
+ ptr = (eth_locator_c *) &bx_null_match;
+ }
+#endif
+#ifdef ETH_FBSD
+ {
+ if (!strcmp(type, "fbsd"))
+ ptr = (eth_locator_c *) &bx_fbsd_match;
+ }
+#endif
+#ifdef ETH_LINUX
+ {
+ if (!strcmp(type, "linux"))
+ ptr = (eth_locator_c *) &bx_linux_match;
+ }
+#endif
+#if HAVE_TUNTAP
+ {
+ if (!strcmp(type, "tuntap"))
+ ptr = (eth_locator_c *) &bx_tuntap_match;
+ }
+#endif
+#if HAVE_ETHERTAP
+ {
+ if (!strcmp(type, "tap"))
+ ptr = (eth_locator_c *) &bx_tap_match;
+ }
+#endif
+#ifdef ETH_WIN32
+ {
+ if(!strcmp(type, "win32"))
+ ptr = (eth_locator_c *) &bx_win32_match;
+ }
+#endif
+#ifdef ETH_TEST
+ {
+ if (!strcmp(type, "test"))
+ ptr = (eth_locator_c *) &bx_test_match;
+ }
+#endif
+ if (ptr)
+ return (ptr->allocate(netif, macaddr, rxh, rxarg));
+#endif
+
+ return (NULL);
+}
+
+#if (HAVE_ETHERTAP==1) || (HAVE_TUNTAP==1)
+
+extern "C" {
+#include <sys/wait.h>
+};
+
+#undef LOG_THIS
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+// This is a utility script used for tuntap or ethertap
+int execute_script( char* scriptname, char* arg1 )
+{
+ int pid,status;
+
+ if (!(pid=fork())) {
+ char filename[BX_PATHNAME_LEN];
+ if ( scriptname[0]=='/' ) {
+ strcpy (filename, scriptname);
+ }
+ else {
+ getcwd (filename, BX_PATHNAME_LEN);
+ strcat (filename, "/");
+ strcat (filename, scriptname);
+ }
+
+ // execute the script
+ BX_INFO(("Executing script '%s %s'",filename,arg1));
+ execle(filename, scriptname, arg1, NULL, NULL);
+
+ // if we get here there has been a problem
+ exit(-1);
+ }
+
+ wait (&status);
+ if (!WIFEXITED(status)) {
+ return -1;
+ }
+ return WEXITSTATUS(status);
+}
+
+#endif // (HAVE_ETHERTAP==1) || (HAVE_TUNTAP==1)
+
+#endif /* if BX_NE2K_SUPPORT */
diff --git a/tools/ioemu/iodev/eth.h b/tools/ioemu/iodev/eth.h
new file mode 100644
index 0000000000..8ac8c6ff3e
--- /dev/null
+++ b/tools/ioemu/iodev/eth.h
@@ -0,0 +1,76 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth.h,v 1.12 2003/04/26 14:48:45 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+// eth.h - see eth_null.cc for implementation details
+
+typedef void (*eth_rx_handler_t)(void *arg, const void *buf, unsigned len);
+
+int execute_script(char *name, char* arg1);
+
+//
+// The eth_pktmover class is used by ethernet chip emulations
+// to interface to the outside world. An instance of this
+// would allow frames to be sent to and received from some
+// entity. An example would be the packet filter on a Unix
+// system, an NDIS driver in promisc mode on WinNT, or maybe
+// a simulated network that talks to another process.
+//
+class eth_pktmover_c {
+public:
+ virtual void sendpkt(void *buf, unsigned io_len) = 0;
+ virtual ~eth_pktmover_c (void) {}
+protected:
+ eth_rx_handler_t rxh; // receive callback
+ void *rxarg;
+};
+
+
+//
+// The eth_locator class is used by pktmover classes to register
+// their name. Chip emulations use the static 'create' method
+// to locate and instantiate a pktmover class.
+//
+class eth_locator_c {
+public:
+ static eth_pktmover_c *create(const char *type, const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+protected:
+ eth_locator_c(const char *type);
+ virtual eth_pktmover_c *allocate(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) = 0;
+private:
+ static eth_locator_c *all;
+ eth_locator_c *next;
+ const char *type;
+};
+
diff --git a/tools/ioemu/iodev/eth_arpback.cc b/tools/ioemu/iodev/eth_arpback.cc
new file mode 100644
index 0000000000..0f30711dfb
--- /dev/null
+++ b/tools/ioemu/iodev/eth_arpback.cc
@@ -0,0 +1,214 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_arpback.cc,v 1.11 2002/11/20 19:06:22 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// eth_arpback.cc - basic ethernet packetmover, only responds to ARP
+
+// Various networking docs:
+// http://www.graphcomp.com/info/rfc/
+// rfc0826: arp
+// rfc0903: rarp
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#if BX_NE2K_SUPPORT && defined(ETH_ARPBACK)
+
+#include "crc32.h"
+#include "eth_packetmaker.h"
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+
+//static const Bit8u external_mac[]={0xB0, 0xC4, 0x20, 0x20, 0x00, 0x00, 0x00};
+//static const Bit8u internal_mac[]={0xB0, 0xC4, 0x20, 0x00, 0x00, 0x00, 0x00};
+//static const Bit8u external_ip[]={ 192, 168, 0, 2, 0x00 };
+//static const Bit8u broadcast_mac[]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
+//static const Bit8u ethtype_arp[]={0x08, 0x06, 0x00};
+
+#define MAX_FRAME_SIZE 2048
+
+//
+// Define the class. This is private to this module
+//
+class bx_arpback_pktmover_c : public eth_pktmover_c {
+public:
+ bx_arpback_pktmover_c(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+private:
+ int rx_timer_index;
+ static void rx_timer_handler(void *);
+ void rx_timer(void);
+ FILE *txlog, *txlog_txt;
+ //Bit8u arpbuf[MAX_FRAME_SIZE];
+ //Bit32u buflen;
+ //bx_bool bufvalid;
+ //CRC_Generator mycrc;
+ eth_ETHmaker packetmaker;
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_arpback_locator_c : public eth_locator_c {
+public:
+ bx_arpback_locator_c(void) : eth_locator_c("arpback") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_arpback_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_arpback_match;
+
+
+//
+// Define the methods for the bx_arpback_pktmover derived class
+//
+
+// the constructor
+bx_arpback_pktmover_c::bx_arpback_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, 1000,
+ 1, 1, "eth_arpback"); // continuous, active
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+ //bufvalid=0;
+ packetmaker.init();
+#if BX_ETH_NULL_LOGGING
+ // Start the rx poll
+ // eventually Bryce wants txlog to dump in pcap format so that
+ // tcpdump -r FILE can read it and interpret packets.
+ txlog = fopen ("ne2k-tx.log", "wb");
+ if (!txlog) BX_PANIC (("open ne2k-tx.log failed"));
+ txlog_txt = fopen ("ne2k-txdump.txt", "wb");
+ if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed"));
+ fprintf (txlog_txt, "arpback packetmover readable log file\n");
+ fprintf (txlog_txt, "net IF = %s\n", netif);
+ fprintf (txlog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (txlog_txt, "\n--\n");
+ fflush (txlog_txt);
+#endif
+}
+
+void
+bx_arpback_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+ if(io_len<MAX_FRAME_SIZE) {
+ eth_packet barney;
+ memcpy(barney.buf,buf,io_len);
+ barney.len=io_len;
+ if(packetmaker.ishandler(barney)) {
+ packetmaker.sendpacket(barney);
+ }
+ /*
+ if(( (!memcmp(buf, external_mac, 6)) || (!memcmp(buf, broadcast_mac, 6)) )
+ && (!memcmp(((Bit8u *)buf)+12, ethtype_arp, 2)) ) {
+ Bit32u tempcrc;
+ memcpy(arpbuf,buf,io_len); //move to temporary buffer
+ memcpy(arpbuf, arpbuf+6, 6); //set destination to sender
+ memcpy(arpbuf+6, external_mac, 6); //set sender to us
+ memcpy(arpbuf+32, arpbuf+22, 10); //move destination to sender
+ memcpy(arpbuf+22, external_mac, 6); //set sender to us
+ memcpy(arpbuf+28, external_ip, 4); //set sender to us
+ arpbuf[21]=2; //make this a reply and not a request
+ tempcrc=mycrc.get_CRC(arpbuf,io_len);
+ memcpy(arpbuf+io_len, &tempcrc, 4);
+ buflen=io_len;//+4
+ bufvalid=1;
+ }
+ */
+ }
+#if BX_ETH_NULL_LOGGING
+ BX_DEBUG (("sendpkt length %u", io_len));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (buf, io_len, 1, txlog);
+ if (n != 1) BX_ERROR (("fwrite to txlog failed", io_len));
+ // dump packet in hex into an ascii log file
+ fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (txlog_txt, "\n");
+ fprintf (txlog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (txlog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (txlog);
+ fflush (txlog_txt);
+#endif
+}
+
+void bx_arpback_pktmover_c::rx_timer_handler (void * this_ptr)
+{
+#if BX_ETH_NULL_LOGGING
+ BX_DEBUG (("rx_timer_handler"));
+#endif
+ bx_arpback_pktmover_c *class_ptr = ((bx_arpback_pktmover_c *)this_ptr);
+
+ class_ptr->rx_timer();
+}
+
+void bx_arpback_pktmover_c::rx_timer (void)
+{
+ int nbytes = 0;
+ struct bpf_hdr *bhdr;
+ eth_packet rubble;
+
+ if(packetmaker.getpacket(rubble)) {
+ //bufvalid=0;
+ void * buf=rubble.buf;
+ unsigned io_len=rubble.len;
+ Bit32u n;
+ fprintf (txlog_txt, "NE2K receiving a packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (txlog_txt, "\n");
+ fprintf (txlog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (txlog_txt, "\n--\n");
+ fflush (txlog_txt);
+
+ (*rxh)(rxarg, buf, io_len);
+ }
+}
+
+#endif /* if BX_NE2K_SUPPORT && defined(ETH_ARPBACK) */
+
diff --git a/tools/ioemu/iodev/eth_fbsd.cc b/tools/ioemu/iodev/eth_fbsd.cc
new file mode 100644
index 0000000000..0c24b9b828
--- /dev/null
+++ b/tools/ioemu/iodev/eth_fbsd.cc
@@ -0,0 +1,385 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_fbsd.cc,v 1.26 2002/11/20 19:06:22 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+// eth_fbsd.cc - A FreeBSD packet filter implementation of
+// an ethernet pktmover. There are some problems and limitations
+// with FreeBSD:
+// - the source address of the frame is overwritten by
+// the hosts's source address. This causes problems with
+// learning bridges - since they cannot determine where
+// BOCHS 'is', they broadcast the frame to all ports.
+// - packets cannot be sent from BOCHS to the host
+// - TCP performance seems to be abysmal; I think this is
+// a timing problem somewhere.
+// - I haven't handled the case where multiple frames arrive
+// in a single BPF read.
+//
+// The /dev/bpf* devices need to be set up with the appropriate
+// permissions for this to work.
+//
+// The config line in .bochsrc should look something like:
+//
+// ne2k: ioaddr=0x280, irq=9, mac=00:a:b:c:1:2, ethmod=fbsd, ethdev=fxp0
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT && defined(ETH_FBSD)
+
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+extern "C" {
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/bpf.h>
+#include <errno.h>
+};
+
+#define BX_BPF_POLL 1000 // Poll for a frame every 250 usecs
+
+#define BX_BPF_BUFSIZ 2048 // enough for an ether frame + bpf hdr
+
+#define BX_BPF_INSNSIZ 8 // number of bpf insns
+
+// template filter for a unicast mac address and all
+// multicast/broadcast frames
+static const struct bpf_insn macfilter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), // A <- P[2:4]
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xaaaaaaaa, 0, 2), // if A != 0xaaaaaaa GOTO LABEL-1
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0), // A <- P[0:2]
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0000aaaa, 2, 0), // if A == 0xaaaa GOTO ACCEPT
+ // LABEL-1
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), // A <- P[0:1]
+ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x01, 0, 1), // if !(A & 1) GOTO LABEL-REJECT
+ // LABEL-ACCEPT
+ BPF_STMT(BPF_RET, 1514), // Accept packet
+ // LABEL-REJECT
+ BPF_STMT(BPF_RET, 0), // Reject packet
+};
+
+// template filter for all frames
+static const struct bpf_insn promiscfilter[] = {
+ BPF_STMT(BPF_RET, 1514)
+};
+
+//
+// Define the class. This is private to this module
+//
+class bx_fbsd_pktmover_c : public eth_pktmover_c {
+public:
+ bx_fbsd_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+
+private:
+ char *fbsd_macaddr[6];
+ int bpf_fd;
+ static void rx_timer_handler(void *);
+ void rx_timer(void);
+ int rx_timer_index;
+ struct bpf_insn filter[BX_BPF_INSNSIZ];
+ FILE *ne2klog, *ne2klog_txt;
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_fbsd_locator_c : public eth_locator_c {
+public:
+ bx_fbsd_locator_c(void) : eth_locator_c("fbsd") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_fbsd_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_fbsd_match;
+
+
+//
+// Define the methods for the bx_fbsd_pktmover derived class
+//
+
+// the constructor
+//
+// Open a bpf file descriptor, and attempt to bind to
+// the specified netif (Replicates libpcap open code)
+//
+bx_fbsd_pktmover_c::bx_fbsd_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+ char device[sizeof "/dev/bpf000"];
+ int tmpfd;
+ int n = 0;
+ struct ifreq ifr;
+ struct bpf_version bv;
+ struct bpf_program bp;
+ u_int v;
+
+ memcpy(fbsd_macaddr, macaddr, 6);
+
+ do {
+ (void)sprintf(device, "/dev/bpf%d", n++);
+ this->bpf_fd = open(device, O_RDWR);
+ BX_DEBUG(("tried %s, returned %d (%s)",device,this->bpf_fd,strerror(errno)));
+ if(errno == EACCES)
+ break;
+ } while (this->bpf_fd == -1);
+
+ if (this->bpf_fd == -1) {
+ BX_PANIC(("eth_freebsd: could not open packet filter: %s", strerror(errno)));
+ return;
+ }
+
+ if (ioctl(this->bpf_fd, BIOCVERSION, (caddr_t)&bv) < 0) {
+ BX_PANIC(("eth_freebsd: could not retrieve bpf version"));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+ if (bv.bv_major != BPF_MAJOR_VERSION || bv.bv_minor < BPF_MINOR_VERSION) {
+ BX_PANIC(("eth_freebsd: bpf version mismatch between compilation and runtime"));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Set buffer size
+ v = BX_BPF_BUFSIZ;
+ if (ioctl(this->bpf_fd, BIOCSBLEN, (caddr_t)&v) < 0) {
+ BX_PANIC(("eth_freebsd: could not set buffer size: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ (void)strncpy(ifr.ifr_name, netif, sizeof(ifr.ifr_name));
+ if (ioctl(this->bpf_fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ BX_PANIC(("eth_freebsd: could not enable interface '%s': %s", netif, strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd == -1;
+ }
+
+ // Verify that the device is an ethernet.
+ if (ioctl(this->bpf_fd, BIOCGDLT, (caddr_t)&v) < 0) {
+ BX_PANIC(("eth_freebsd: could not retrieve datalink type: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+ if (v != DLT_EN10MB) {
+ BX_PANIC(("eth_freebsd: incorrect datalink type %d, expected 10mb ethernet", v));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Put the device into promisc mode. This could be optimised
+ // to filter on a MAC address, broadcast, and all-multi,
+ // but this will do for now.
+ //
+ if (ioctl(this->bpf_fd, BIOCPROMISC, NULL) < 0) {
+ BX_PANIC(("eth_freebsd: could not enable promisc mode: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Set up non-blocking i/o
+ v = 1;
+ if (ioctl(this->bpf_fd, FIONBIO, &v) < 0) {
+ BX_PANIC(("eth_freebsd: could not enable non-blocking i/o: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Install a filter
+#ifdef notdef
+ memcpy(&this->filter, promiscfilter, sizeof(promiscfilter));
+ bp.bf_len = 1;
+#else
+ memcpy(&this->filter, macfilter, sizeof(macfilter));
+ this->filter[1].k =
+ (macaddr[2] & 0xff) << 24 |
+ (macaddr[3] & 0xff) << 16 |
+ (macaddr[4] & 0xff) << 8 |
+ (macaddr[5] & 0xff);
+ this->filter[3].k =
+ (macaddr[0] & 0xff) << 8 |
+ (macaddr[1] & 0xff);
+ bp.bf_len = 8;
+#endif
+ bp.bf_insns = &this->filter[0];
+ if (ioctl(this->bpf_fd, BIOCSETF, &bp) < 0) {
+ BX_PANIC(("eth_freebsd: could not set filter: %s", strerror(errno)));
+ close(this->bpf_fd);
+ this->bpf_fd = -1;
+ return;
+ }
+
+ // Start the rx poll
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, BX_BPF_POLL,
+ 1, 1, "eth_fbsd"); // continuous, active
+
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+
+#if BX_ETH_FBSD_LOGGING
+ // eventually Bryce wants ne2klog to dump in pcap format so that
+ // tcpdump -r FILE can read it and interpret packets.
+ ne2klog = fopen ("ne2k.raw", "wb");
+ if (!ne2klog) BX_PANIC (("open ne2k-tx.log failed"));
+ ne2klog_txt = fopen ("ne2k.txt", "wb");
+ if (!ne2klog_txt) BX_PANIC (("open ne2k-txdump.txt failed"));
+ fprintf (ne2klog_txt, "null packetmover readable log file\n");
+ fprintf (ne2klog_txt, "net IF = %s\n", netif);
+ fprintf (ne2klog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (ne2klog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (ne2klog_txt, "\n--\n");
+ fflush (ne2klog_txt);
+#endif
+}
+
+// the output routine - called with pre-formatted ethernet frame.
+void
+bx_fbsd_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+#if BX_ETH_FBSD_LOGGING
+ BX_DEBUG (("sendpkt length %u", io_len));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (buf, io_len, 1, ne2klog);
+ if (n != 1) BX_ERROR (("fwrite to ne2klog failed", io_len));
+ // dump packet in hex into an ascii log file
+ fprintf (ne2klog_txt, "NE2K TX packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (ne2klog_txt, "\n");
+ fprintf (ne2klog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (ne2klog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (ne2klog);
+ fflush (ne2klog_txt);
+#endif
+ int status;
+
+ if (this->bpf_fd != -1)
+ status = write(this->bpf_fd, buf, io_len);
+}
+
+// The receive poll process
+void
+bx_fbsd_pktmover_c::rx_timer_handler(void *this_ptr)
+{
+ bx_fbsd_pktmover_c *class_ptr = (bx_fbsd_pktmover_c *) this_ptr;
+
+ class_ptr->rx_timer();
+}
+
+
+void
+bx_fbsd_pktmover_c::rx_timer(void)
+{
+ int nbytes = 0;
+ unsigned char rxbuf[BX_BPF_BUFSIZ];
+ struct bpf_hdr *bhdr;
+ struct bpf_stat bstat;
+ static struct bpf_stat previous_bstat;
+ int counter = 10;
+#define phdr ((unsigned char*)bhdr)
+
+ bhdr = (struct bpf_hdr *) rxbuf;
+ nbytes = read(this->bpf_fd, rxbuf, sizeof(rxbuf));
+
+ while (phdr < (rxbuf + nbytes)) {
+ if (ioctl(this->bpf_fd, BIOCGSTATS, &bstat) < 0) {
+ BX_PANIC(("eth_freebsd: could not stat filter: %s", strerror(errno)));
+ }
+ if (bstat.bs_drop > previous_bstat.bs_drop) {
+ BX_INFO (("eth_freebsd: %d packets dropped by the kernel.",
+ bstat.bs_drop - previous_bstat.bs_drop));
+ }
+ previous_bstat = bstat;
+ if (bhdr->bh_caplen < 20 || bhdr->bh_caplen > 1514) {
+ BX_ERROR(("eth_freebsd: received too weird packet length: %d", bhdr->bh_caplen));
+ }
+
+ // filter out packets sourced from this node
+ if (memcmp(bhdr + bhdr->bh_hdrlen + 6, this->fbsd_macaddr, 6)) {
+ (*rxh)(rxarg, phdr + bhdr->bh_hdrlen, bhdr->bh_caplen);
+ }
+
+#if BX_ETH_FBSD_LOGGING
+ /// hey wait there is no receive data with a NULL ethernet, is there....
+ BX_DEBUG (("receive packet length %u", nbytes));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ if (1 != fwrite (bhdr, bhdr->bh_caplen, 1, ne2klog)) {
+ BX_PANIC (("fwrite to ne2klog failed: %s", strerror(errno)));
+ }
+ // dump packet in hex into an ascii log file
+ fprintf (this->ne2klog_txt, "NE2K RX packet, length %u\n", bhdr->bh_caplen);
+ Bit8u *charrxbuf = (Bit8u *)rxbuf;
+ int n;
+ for (n=0; n<bhdr->bh_caplen; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (this->ne2klog_txt, "\n");
+ fprintf (this->ne2klog_txt, "%02x ", phdr[n]);
+ }
+ fprintf (this->ne2klog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (this->ne2klog);
+ fflush (this->ne2klog_txt);
+#endif
+
+ // Advance bhdr and phdr pointers to next packet
+ bhdr = (struct bpf_hdr*) ((char*) bhdr + BPF_WORDALIGN(bhdr->bh_hdrlen + bhdr->bh_caplen));
+ }
+}
+
+#endif /* if BX_NE2K_SUPPORT && defined(ETH_FBSD) */
+
diff --git a/tools/ioemu/iodev/eth_linux.cc b/tools/ioemu/iodev/eth_linux.cc
new file mode 100644
index 0000000000..ec5f1fee73
--- /dev/null
+++ b/tools/ioemu/iodev/eth_linux.cc
@@ -0,0 +1,286 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_linux.cc,v 1.14 2003/02/16 19:35:57 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+// eth_linux.cc - A Linux socket filter adaptation of the FreeBSD BPF driver
+// <splite@purdue.edu> 21 June 2001
+//
+// Problems and limitations:
+// - packets cannot be sent from BOCHS to the host
+// - Linux kernel sometimes gets network watchdog timeouts under emulation
+// - author doesn't know C++
+//
+// The config line in .bochsrc should look something like:
+//
+// ne2k: ioaddr=0x280, irq=10, mac=00:a:b:c:1:2, ethmod=linux, ethdev=eth0
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT && defined (ETH_LINUX)
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+extern "C" {
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <linux/types.h>
+#include <linux/filter.h>
+};
+
+#define BX_PACKET_POLL 1000 // Poll for a frame every 1000 usecs
+
+#define BX_PACKET_BUFSIZ 2048 // Enough for an ether frame
+
+// template filter for a unicast mac address and all
+// multicast/broadcast frames
+static const struct sock_filter macfilter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xaaaaaaaa, 0, 2),
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0000aaaa, 2, 0),
+ BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0),
+ BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x01, 0, 1),
+ BPF_STMT(BPF_RET, 1514),
+ BPF_STMT(BPF_RET, 0),
+};
+#define BX_LSF_ICNT 8 // number of lsf instructions in macfilter
+
+#if 0
+// template filter for all frames
+static const struct sock_filter promiscfilter[] = {
+ BPF_STMT(BPF_RET, 1514)
+};
+#endif
+
+//
+// Define the class. This is private to this module
+//
+class bx_linux_pktmover_c : public eth_pktmover_c {
+public:
+ bx_linux_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+
+private:
+ unsigned char *linux_macaddr[6];
+ int fd;
+ int ifindex;
+ static void rx_timer_handler(void *);
+ void rx_timer(void);
+ int rx_timer_index;
+ struct sock_filter filter[BX_LSF_ICNT];
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_linux_locator_c : public eth_locator_c {
+public:
+ bx_linux_locator_c(void) : eth_locator_c("linux") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_linux_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_linux_match;
+
+
+//
+// Define the methods for the bx_linux_pktmover derived class
+//
+
+// the constructor
+//
+bx_linux_pktmover_c::bx_linux_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+ struct sockaddr_ll sll;
+ struct packet_mreq mr;
+ struct ifreq ifr;
+ struct sock_fprog fp;
+
+ memcpy(linux_macaddr, macaddr, 6);
+
+ // Open packet socket
+ //
+ if ((this->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) {
+ if (errno == EACCES)
+ BX_PANIC(("eth_linux: must be root or have CAP_NET_RAW capability to open socket"));
+ else
+ BX_PANIC(("eth_linux: could not open socket: %s", strerror(errno)));
+ this->fd = -1;
+ return;
+ }
+
+ // Translate interface name to index
+ //
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, netif);
+ if (ioctl(this->fd, SIOCGIFINDEX, &ifr) == -1) {
+ BX_PANIC(("eth_linux: could not get index for interface '%s'\n", netif));
+ close(fd);
+ this->fd = -1;
+ return;
+ }
+ this->ifindex = ifr.ifr_ifindex;
+
+
+ // Bind to given interface
+ //
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = AF_PACKET;
+ sll.sll_ifindex = this->ifindex;
+ if (bind(fd, (struct sockaddr *)&sll, (socklen_t)sizeof(sll)) == -1) {
+ BX_PANIC(("eth_linux: could not bind to interface '%s': %s\n", netif, strerror(errno)));
+ close(fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Put the device into promisc mode.
+ //
+ memset(&mr, 0, sizeof(mr));
+ mr.mr_ifindex = this->ifindex;
+ mr.mr_type = PACKET_MR_PROMISC;
+ if (setsockopt(this->fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, (void *)&mr, (socklen_t)sizeof(mr)) == -1) {
+ BX_PANIC(("eth_linux: could not enable promisc mode: %s\n", strerror(errno)));
+ close(this->fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Set up non-blocking i/o
+ if (fcntl(this->fd, F_SETFL, O_NONBLOCK) == -1) {
+ BX_PANIC(("eth_linux: could not set non-blocking i/o on socket"));
+ close(this->fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Install a filter
+#ifdef notdef
+ memcpy(&this->filter, promiscfilter, sizeof(promiscfilter));
+ fp.len = 1;
+#endif
+ memcpy(&this->filter, macfilter, sizeof(macfilter));
+ this->filter[1].k = (macaddr[2] & 0xff) << 24 | (macaddr[3] & 0xff) << 16 |
+ (macaddr[4] & 0xff) << 8 | (macaddr[5] & 0xff);
+ this->filter[3].k = (macaddr[0] & 0xff) << 8 | (macaddr[1] & 0xff);
+ fp.len = BX_LSF_ICNT;
+ fp.filter = this->filter;
+ BX_INFO(("eth_linux: fp.len=%d fp.filter=%x", fp.len, (unsigned) fp.filter));
+ if (setsockopt(this->fd, SOL_SOCKET, SO_ATTACH_FILTER, &fp, sizeof(fp)) < 0) {
+ BX_PANIC(("eth_linux: could not set socket filter: %s", strerror(errno)));
+ close(this->fd);
+ this->fd = -1;
+ return;
+ }
+
+ // Start the rx poll
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, BX_PACKET_POLL,
+ 1, 1, "eth_linux"); // continuous, active
+
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+ BX_INFO(("eth_linux: enabled NE2K emulation on interface %s", netif));
+}
+
+// the output routine - called with pre-formatted ethernet frame.
+void
+bx_linux_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+ int status;
+
+ if (this->fd != -1) {
+ status = write(this->fd, buf, io_len);
+ if (status == -1)
+ BX_INFO(("eth_linux: write failed: %s", strerror(errno)));
+ }
+}
+
+// The receive poll process
+void
+bx_linux_pktmover_c::rx_timer_handler(void *this_ptr)
+{
+ bx_linux_pktmover_c *class_ptr = (bx_linux_pktmover_c *) this_ptr;
+
+ class_ptr->rx_timer();
+}
+
+void
+bx_linux_pktmover_c::rx_timer(void)
+{
+ int nbytes = 0;
+ Bit8u rxbuf[BX_PACKET_BUFSIZ];
+ struct sockaddr_ll sll;
+ socklen_t fromlen;
+//static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+
+ if (this->fd == -1)
+ return;
+
+ fromlen = sizeof(sll);
+ nbytes = recvfrom(this->fd, rxbuf, sizeof(rxbuf), 0, (struct sockaddr *)&sll, &fromlen);
+
+ if (nbytes == -1) {
+ if (errno != EAGAIN)
+ BX_INFO(("eth_linux: error receiving packet: %s\n", strerror(errno)));
+ return;
+ }
+
+ // this should be done with LSF someday
+ // filter out packets sourced by us
+ if (memcmp(sll.sll_addr, this->linux_macaddr, 6) == 0)
+ return;
+ // let through broadcast, multicast, and our mac address
+// if ((memcmp(rxbuf, bcast_addr, 6) == 0) || (memcmp(rxbuf, this->linux_macaddr, 6) == 0) || rxbuf[0] & 0x01) {
+ BX_DEBUG(("eth_linux: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11]));
+ (*rxh)(rxarg, rxbuf, nbytes);
+// }
+}
+#endif /* if BX_NE2K_SUPPORT && defined ETH_LINUX */
diff --git a/tools/ioemu/iodev/eth_null.cc b/tools/ioemu/iodev/eth_null.cc
new file mode 100644
index 0000000000..11162798ef
--- /dev/null
+++ b/tools/ioemu/iodev/eth_null.cc
@@ -0,0 +1,164 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_null.cc,v 1.13 2002/11/20 19:06:23 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// eth_null.cc - skeleton code for an ethernet pktmover
+
+// Various networking docs:
+// http://www.graphcomp.com/info/rfc/
+// rfc0826: arp
+// rfc0903: rarp
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT
+
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+
+//
+// Define the class. This is private to this module
+//
+class bx_null_pktmover_c : public eth_pktmover_c {
+public:
+ bx_null_pktmover_c(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+private:
+ int rx_timer_index;
+ static void rx_timer_handler(void *);
+ FILE *txlog, *txlog_txt, *rxlog, *rxlog_txt;
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_null_locator_c : public eth_locator_c {
+public:
+ bx_null_locator_c(void) : eth_locator_c("null") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_null_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_null_match;
+
+
+//
+// Define the methods for the bx_null_pktmover derived class
+//
+
+// the constructor
+bx_null_pktmover_c::bx_null_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+#if BX_ETH_NULL_LOGGING
+ // Start the rx poll
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, 1000,
+ 1, 1, "eth_null"); // continuous, active
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+ // eventually Bryce wants txlog to dump in pcap format so that
+ // tcpdump -r FILE can read it and interpret packets.
+ txlog = fopen ("ne2k-tx.log", "wb");
+ if (!txlog) BX_PANIC (("open ne2k-tx.log failed"));
+ txlog_txt = fopen ("ne2k-txdump.txt", "wb");
+ if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed"));
+ fprintf (txlog_txt, "null packetmover readable log file\n");
+ fprintf (txlog_txt, "net IF = %s\n", netif);
+ fprintf (txlog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (txlog_txt, "\n--\n");
+ fflush (txlog_txt);
+#endif
+}
+
+void
+bx_null_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+#if BX_ETH_NULL_LOGGING
+ BX_DEBUG (("sendpkt length %u", io_len));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ unsigned int n = fwrite (buf, io_len, 1, txlog);
+ if (n != 1) BX_ERROR (("fwrite to txlog failed, io_len = %u", io_len));
+ // dump packet in hex into an ascii log file
+ fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (txlog_txt, "\n");
+ fprintf (txlog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (txlog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (txlog);
+ fflush (txlog_txt);
+#endif
+}
+
+void bx_null_pktmover_c::rx_timer_handler (void *this_ptr)
+{
+#if BX_ETH_NULL_LOGGING
+ /// hey wait there is no receive data with a NULL ethernet, is there....
+
+ int io_len = 0;
+ Bit8u buf[1];
+ bx_null_pktmover_c *class_ptr = (bx_null_pktmover_c *) this_ptr;
+ if (io_len > 0) {
+ BX_DEBUG (("receive packet length %u", io_len));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (buf, io_len, 1, class_ptr->rxlog);
+ if (n != 1) BX_ERROR (("fwrite to rxlog failed, io_len = %u", io_len));
+ // dump packet in hex into an ascii log file
+ fprintf (class_ptr->rxlog_txt, "NE2K transmitting a packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (class_ptr->rxlog_txt, "\n");
+ fprintf (class_ptr->rxlog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (class_ptr->rxlog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (class_ptr->rxlog);
+ fflush (class_ptr->rxlog_txt);
+ }
+#endif
+}
+
+#endif /* if BX_NE2K_SUPPORT */
diff --git a/tools/ioemu/iodev/eth_packetmaker.cc b/tools/ioemu/iodev/eth_packetmaker.cc
new file mode 100644
index 0000000000..5ed5e47c8c
--- /dev/null
+++ b/tools/ioemu/iodev/eth_packetmaker.cc
@@ -0,0 +1,184 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_packetmaker.cc,v 1.8 2002/11/20 19:06:23 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#if BX_NE2K_SUPPORT && defined(ETH_ARPBACK)
+
+#include "eth_packetmaker.h"
+
+
+bx_bool sendable(const eth_packet& outpacket) {
+ //FINISH ME!
+}
+
+Bit32u eth_IPmaker::datalen(const eth_packet& outpacket) {
+ Bit32u out;
+ out=((outpacket.buf[16]<<8)+outpacket.buf[17])-(4*(0xF & outpacket.buf[14]));
+ return out;
+}
+
+const Bit8u * eth_IPmaker::datagram(const eth_packet& outpacket) {
+ const Bit8u * out;
+ out=outpacket.buf+14+(4*(0xF & outpacket.buf[14]));
+ return out;
+}
+
+Bit32u eth_IPmaker::build_packet_header(Bit32u source, Bit32u dest, Bit8u protocol, Bit32u datalen) {
+ Bit32u temp;
+ Bit32u i;
+ memcpy(pending.buf,internal_mac,6);
+ memcpy(pending.buf+6,external_mac,6);
+ memcpy(pending.buf+12,ethtype_ip,2);
+ pending.buf[14]=0x45;
+ pending.buf[15]=0;
+ temp=datalen+20;
+ pending.buf[16]=(temp>>8) & 0xFF;
+ pending.buf[17]=temp & 0xFF;
+ pending.buf[18]=0;
+ pending.buf[19]=0;
+ pending.buf[20]=0;
+ pending.buf[21]=0;
+ pending.buf[22]=30;
+ pending.buf[23]=protocol;
+ pending.buf[24]=0;
+ pending.buf[25]=0;
+ pending.buf[26]=(source>>24) & 0xFF;
+ pending.buf[27]=(source>>16) & 0xFF;
+ pending.buf[28]=(source>>8) & 0xFF;
+ pending.buf[29]=(source) & 0xFF;
+ pending.buf[30]=(dest>>24) & 0xFF;
+ pending.buf[31]=(dest>>16) & 0xFF;
+ pending.buf[32]=(dest>>8) & 0xFF;
+ pending.buf[33]=(dest) & 0xFF;
+ //Compute Checksum
+ temp=0;
+ for(i=14;i<34;i=i+2) {
+ Bit32u addee=pending.buf[i];
+ addee=(addee<<8) & pending.buf[i+1];
+ temp=temp+addee;
+ }
+ temp=(temp>>16)+(temp&0xFFFF);
+ temp=(temp>>16)+(temp&0xFFFF);
+ pending.buf[24]=~(Bit8u)((temp>>8) & 0xFF);
+ pending.buf[25]=~(Bit8u)(temp & 0xFF);
+ return(34);
+}
+
+Bit8u eth_IPmaker::protocol(const eth_packet& outpacket) {
+ Bit8u out;
+ out=0xFF & *(outpacket.buf+23);
+}
+
+Bit32u eth_IPmaker::source(const eth_packet& outpacket) {
+ Bit32u out;
+ out=0xFF & *(outpacket.buf+26);
+ out=(out<<8) | (0xFF & *(outpacket.buf+27));
+ out=(out<<8) | (0xFF & *(outpacket.buf+28));
+ out=(out<<8) | (0xFF & *(outpacket.buf+29));
+ return out;
+}
+
+Bit32u eth_IPmaker::destination(const eth_packet& outpacket) {
+ Bit32u out;
+ out=0xFF & *(outpacket.buf+30);
+ out=(out<<8) | (0xFF & *(outpacket.buf+31));
+ out=(out<<8) | (0xFF & *(outpacket.buf+32));
+ out=(out<<8) | (0xFF & *(outpacket.buf+33));
+ return out;
+}
+
+void eth_IPmaker::init(void) {
+ is_pending=0;
+}
+
+void
+eth_ETHmaker::init(void) {
+ arper.init();
+}
+
+bx_bool
+eth_ETHmaker::getpacket(eth_packet& inpacket) {
+ return arper.getpacket(inpacket);
+}
+
+bx_bool
+eth_ETHmaker::ishandler(const eth_packet& outpacket) {
+ if((outpacket.len>=60) &&
+ ( (!memcmp(outpacket.buf, external_mac, 6))
+ || (!memcmp(outpacket.buf, broadcast_mac, 6)) ) &&
+ ( (!memcmp(outpacket.buf+12, ethtype_arp, 2)) ||
+ (!memcmp(outpacket.buf+12, ethtype_ip, 2)) ) &&
+ (outpacket.len<PACKET_BUF_SIZE)
+ ) {
+ return 1;
+ }
+ return 0;
+}
+
+bx_bool
+eth_ETHmaker::sendpacket(const eth_packet& outpacket) {
+ return arper.sendpacket(outpacket);
+}
+
+
+
+void
+eth_ARPmaker::init(void) {
+ is_pending=0;
+ pending.len=0;
+}
+
+bx_bool
+eth_ARPmaker::getpacket(eth_packet& inpacket) {
+ if(is_pending) {
+ memcpy(inpacket.buf,pending.buf,pending.len);
+ inpacket.len=pending.len;
+ is_pending=0;
+ return 1;
+ }
+ return 0;
+}
+
+bx_bool
+eth_ARPmaker::ishandler(const eth_packet& outpacket) {
+ if((outpacket.len>=60) &&
+ (!memcmp(outpacket.buf+12, ethtype_arp, 2)) &&
+ (outpacket.len<PACKET_BUF_SIZE) &&
+ ( (!memcmp(outpacket.buf, external_mac, 6))
+ || (!memcmp(outpacket.buf, broadcast_mac, 6)) ) &&
+ (!memcmp(outpacket.buf+38, external_ip, 4))
+ ) {
+ return 1;
+ }
+ return 0;
+}
+
+bx_bool
+eth_ARPmaker::sendpacket(const eth_packet& outpacket) {
+ if(is_pending || !ishandler(outpacket)) {
+ return 0;
+ } else {
+ Bit32u tempcrc;
+ memcpy(pending.buf,outpacket.buf,outpacket.len); //move to temporary buffer
+ memcpy(pending.buf, pending.buf+6, 6); //set destination to sender
+ memcpy(pending.buf+6, external_mac, 6); //set sender to us
+ memcpy(pending.buf+32, pending.buf+22, 10); //move destination to sender
+ memcpy(pending.buf+22, external_mac, 6); //set sender to us
+ memcpy(pending.buf+28, external_ip, 4); //set sender to us
+ pending.buf[21]=2; //make this a reply and not a request
+ //tempcrc=mycrc.get_CRC(pending.buf,len);
+ //memcpy(pending.buf+len, &tempcrc, 4);
+ pending.len=outpacket.len; //+4
+ is_pending=1;
+ return 1;
+ }
+}
+
+#endif /* if BX_NE2K_SUPPORT && defined(ETH_ARPBACK) */
diff --git a/tools/ioemu/iodev/eth_packetmaker.h b/tools/ioemu/iodev/eth_packetmaker.h
new file mode 100644
index 0000000000..d442325a9f
--- /dev/null
+++ b/tools/ioemu/iodev/eth_packetmaker.h
@@ -0,0 +1,135 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_packetmaker.h,v 1.6 2002/10/25 11:44:39 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+
+#ifndef _ETH_PACKETMAKER_H_
+#define _ETH_PACKETMAKER_H_
+
+#include "../config.h"
+
+#ifdef ETH_ARPBACK
+
+#define PACKET_BUF_SIZE 2048
+static const Bit8u internal_mac[]={0xB0, 0xC4, 0x20, 0x20, 0x00, 0x00, 0x00};
+static const Bit8u external_mac[]={0xB0, 0xC4, 0x20, 0x20, 0x00, 0x00, 0x00};
+static const Bit8u external_ip[]={ 192, 168, 0, 2, 0x00 };
+static const Bit8u broadcast_mac[]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
+static const Bit8u ethtype_arp[]={0x08, 0x06, 0x00};
+static const Bit8u ethtype_ip[]={0x08, 0x00, 0x00};
+static const Bit8u prot_udp=17;
+static const Bit8u prot_tcp=6;
+
+
+class eth_packet {
+public:
+ Bit8u buf[PACKET_BUF_SIZE];
+ Bit32u len;
+};
+
+
+class eth_packetmaker {
+public:
+ virtual bx_bool getpacket(eth_packet& inpacket) = 0;
+ virtual bx_bool ishandler(const eth_packet& outpacket) = 0;
+ virtual bx_bool sendpacket(const eth_packet& outpacket) = 0;
+};
+
+
+class eth_ARPmaker : public eth_packetmaker {
+public:
+ void init(void);
+ bx_bool ishandler(const eth_packet& outpacket);
+ bx_bool sendpacket(const eth_packet& outpacket);
+ bx_bool getpacket(eth_packet& inpacket);
+private:
+ eth_packet pending;
+ bx_bool is_pending;
+};
+
+
+class eth_IPmaker : eth_packetmaker {
+public:
+ void init(void);
+ virtual bx_bool ishandler(const eth_packet& outpacket)=0;
+ virtual bx_bool sendpacket(const eth_packet& outpacket)=0;
+ virtual bx_bool getpacket(eth_packet& inpacket)=0;
+
+protected:
+ bx_bool sendable(const eth_packet& outpacket);
+
+ Bit32u source(const eth_packet& outpacket);
+ Bit32u destination(const eth_packet& outpacket);
+ Bit8u protocol(const eth_packet& outpacket);
+
+ const Bit8u * datagram(const eth_packet& outpacket);
+ Bit32u datalen(const eth_packet& outpacket);
+
+ //Build a header in pending, return header length in bytes.
+ Bit32u build_packet_header(Bit32u source, Bit32u dest, Bit8u protocol, Bit32u datalen);
+
+ eth_packet pending;
+ bx_bool is_pending;
+
+ //Bit8u Version; //=4 (4 bits)
+ //It better be!
+
+ //Bit8u IHL; //Header length in 32-bit bytes (4 bits)
+ //Used to strip layer
+
+ //Bit8u Type_of_Service; //not relevent, set to 0;
+ //Ignore on receive, set to 0 on send.
+
+ //Bit16u Total_Length;//length of the datagram in octets. use 576 or less;
+ //Use 576 or less on send.
+ //Use to get length on receive
+
+ //Bit16u Identification;//Identifier for assembling fragments
+ //Ignore, we'll drop fragments
+
+ //Bit8u Flags;//0,Don't fragment, More Fragments (vs last fragment)
+ //Set to 0 on send
+ //Drop if more fragments set.
+
+ //Bit16u Fragment Offset;//where in the datagram this fragment belongs
+ //Should be 0 for send and receive.
+
+ //Bit8u TTL;//Set to something sorta big.
+ //Shouldn't be 0 on receive
+ //Set to something big on send
+
+ //Bit8u Protocol;
+ //Defines Protocol.
+ //TCP=?, UDP=?
+
+ //Bit16u Header_Checksum;//16-bit one's complement of the one's complement
+ //sum of all 16-bit words in header;
+ //Could check on receive, must set on send.
+
+ //Bit32u Source;//source address
+ //Bit32u Destination;//destination address
+};
+
+/*
+class eth_TCPmaker : eth_packetmaker {
+};
+
+class eth_UDPmaker : eth_packetmaker {
+};
+*/
+
+class eth_ETHmaker : public eth_packetmaker {
+public:
+ //handles all packets to a MAC addr.
+ void init(void);
+ virtual bx_bool getpacket(eth_packet& inpacket);
+ virtual bx_bool ishandler(const eth_packet& outpacket);
+ virtual bx_bool sendpacket(const eth_packet& outpacket);
+private:
+ eth_ARPmaker arper;
+};
+
+
+#endif // ETH_ARPBACK
+#endif // _ETH_PACKETMAKER_H_
+
diff --git a/tools/ioemu/iodev/eth_tap.cc b/tools/ioemu/iodev/eth_tap.cc
new file mode 100644
index 0000000000..cb1bf1df89
--- /dev/null
+++ b/tools/ioemu/iodev/eth_tap.cc
@@ -0,0 +1,370 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_tap.cc,v 1.16 2003/10/02 11:33:41 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// eth_tap.cc - TAP interface by Bryce Denney
+//
+// Here's how to get this working. On the host machine:
+// $ su root
+// # /sbin/insmod ethertap
+// Using /lib/modules/2.2.14-5.0/net/ethertap.o
+// # mknod /dev/tap0 c 36 16 # if not already there
+// # /sbin/ifconfig tap0 10.0.0.1
+// # /sbin/route add -host 10.0.0.2 gw 10.0.0.1
+//
+// Now you have a tap0 device which you can on the ifconfig output. The
+// tap0 interface has the IP address of 10.0.0.1. The bochs machine will have
+// the IP address 10.0.0.2.
+//
+// Compile a bochs version from March 8, 2002 or later with --enable-ne2000.
+// Add this ne2k line to your .bochsrc to activate the tap device.
+// ne2k: ioaddr=0x280, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0
+// Don't change the mac or ethmod!
+//
+// Boot up DLX Linux in Bochs. Log in as root and then type the following
+// commands to set up networking:
+// # ifconfig eth0 10.0.0.2
+// # route add -net 10.0.0.0
+// # route add default gw 10.0.0.1
+// Now you should be able to ping from guest OS to your host machine, if
+// you give its IP number. I'm still having trouble with pings from the
+// host machine to the guest, so something is still not right. Symptoms: I
+// ping from the host to the guest's IP address 10.0.0.2. With tcpdump I can
+// see the ping going to Bochs, and then the ping reply coming from Bochs.
+// But the ping program itself does not see the responses....well every
+// once in a while it does, like 1 in 60 pings.
+//
+// host$ ping 10.0.0.2
+// PING 10.0.0.2 (10.0.0.2) from 10.0.0.1 : 56(84) bytes of data.
+//
+// Netstat output:
+// 20:29:59.018776 fe:fd:0:0:0:0 fe:fd:0:0:0:1 0800 98: 10.0.0.1 > 10.0.0.2: icmp: echo request
+// 4500 0054 2800 0000 4001 3ea7 0a00 0001
+// 0a00 0002 0800 09d3 a53e 0400 9765 893c
+// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213
+// 1415 1617 1819
+// 20:29:59.023017 fe:fd:0:0:0:1 fe:fd:0:0:0:0 0800 98: 10.0.0.2 > 10.0.0.1: icmp: echo reply
+// 4500 0054 004a 0000 4001 665d 0a00 0002
+// 0a00 0001 0000 11d3 a53e 0400 9765 893c
+// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213
+// 1415 1617 1819
+//
+// I suspect it may be related to the fact that ping 10.0.0.1 from the
+// host also doesn't work. Why wouldn't the host respond to its own IP
+// address on the tap0 device?
+//
+// Theoretically, if you set up packet forwarding (with masquerading) on the
+// host, you should be able to get Bochs talking to anyone on the internet.
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT
+
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#ifndef __APPLE__
+#include <sys/poll.h>
+#endif
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#if defined(__FreeBSD__) || defined(__APPLE__) // Should be fixed for other *BSD
+#include <net/if.h>
+#else
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/if.h>
+#endif
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define TAP_VIRTUAL_HW_ADDR 0xDEADBEEF
+#define BX_ETH_TAP_LOGGING 1
+#define BX_PACKET_BUFSIZ 2048 // Enough for an ether frame
+
+//
+// Define the class. This is private to this module
+//
+class bx_tap_pktmover_c : public eth_pktmover_c {
+public:
+ bx_tap_pktmover_c(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+private:
+ int fd;
+ int rx_timer_index;
+ static void rx_timer_handler(void *);
+ void rx_timer ();
+ FILE *txlog, *txlog_txt, *rxlog, *rxlog_txt;
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_tap_locator_c : public eth_locator_c {
+public:
+ bx_tap_locator_c(void) : eth_locator_c("tap") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_tap_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_tap_match;
+
+
+//
+// Define the methods for the bx_tap_pktmover derived class
+//
+
+// the constructor
+bx_tap_pktmover_c::bx_tap_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+ int flags;
+ char filename[BX_PATHNAME_LEN];
+ if (strncmp (netif, "tap", 3) != 0) {
+ BX_PANIC (("eth_tap: interface name (%s) must be tap0..tap15", netif));
+ }
+ sprintf (filename, "/dev/%s", netif);
+
+#if defined(__linux__)
+ // check if the TAP devices is running, and turn on ARP. This is based
+ // on code from the Mac-On-Linux project. http://http://www.maconlinux.org/
+ int sock = socket( AF_INET, SOCK_DGRAM, 0 );
+ if (sock < 0) {
+ BX_PANIC (("socket creation: %s", strerror(errno)));
+ return;
+ }
+ struct ifreq ifr;
+ memset( &ifr, 0, sizeof(ifr) );
+ strncpy( ifr.ifr_name, netif, sizeof(ifr.ifr_name) );
+ if( ioctl( sock, SIOCGIFFLAGS, &ifr ) < 0 ){
+ BX_PANIC (("SIOCGIFFLAGS on %s: %s", netif, strerror (errno)));
+ close(sock);
+ return;
+ }
+ if( !(ifr.ifr_flags & IFF_RUNNING ) ){
+ BX_PANIC (("%s device is not running", netif));
+ close(sock);
+ return;
+ }
+ if( (ifr.ifr_flags & IFF_NOARP ) ){
+ BX_INFO (("turn on ARP for %s device", netif));
+ ifr.ifr_flags &= ~IFF_NOARP;
+ if( ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0 ) {
+ BX_PANIC (("SIOCSIFFLAGS: %s", strerror(errno)));
+ close(sock);
+ return;
+ }
+ }
+ close(sock);
+#endif
+
+ fd = open (filename, O_RDWR);
+ if (fd < 0) {
+ BX_PANIC (("open failed on %s: %s", netif, strerror (errno)));
+ return;
+ }
+
+ /* set O_ASYNC flag so that we can poll with read() */
+ if ((flags = fcntl( fd, F_GETFL)) < 0) {
+ BX_PANIC (("getflags on tap device: %s", strerror (errno)));
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl( fd, F_SETFL, flags ) < 0) {
+ BX_PANIC (("set tap device flags: %s", strerror (errno)));
+ }
+
+ BX_INFO (("eth_tap: opened %s device", netif));
+
+ /* Execute the configuration script */
+ char intname[IFNAMSIZ];
+ strcpy(intname,netif);
+ char *scriptname=bx_options.ne2k.Oscript->getptr();
+ if((scriptname != NULL)
+ &&(strcmp(scriptname, "") != 0)
+ &&(strcmp(scriptname, "none") != 0)) {
+ if (execute_script(scriptname, intname) < 0)
+ BX_ERROR (("execute script '%s' on %s failed", scriptname, intname));
+ }
+
+ // Start the rx poll
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, 1000,
+ 1, 1, "eth_tap"); // continuous, active
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+#if BX_ETH_TAP_LOGGING
+ // eventually Bryce wants txlog to dump in pcap format so that
+ // tcpdump -r FILE can read it and interpret packets.
+ txlog = fopen ("ne2k-tx.log", "wb");
+ if (!txlog) BX_PANIC (("open ne2k-tx.log failed"));
+ txlog_txt = fopen ("ne2k-txdump.txt", "wb");
+ if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed"));
+ fprintf (txlog_txt, "tap packetmover readable log file\n");
+ fprintf (txlog_txt, "net IF = %s\n", netif);
+ fprintf (txlog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (txlog_txt, "\n--\n");
+ fflush (txlog_txt);
+
+ rxlog = fopen ("ne2k-rx.log", "wb");
+ if (!rxlog) BX_PANIC (("open ne2k-rx.log failed"));
+ rxlog_txt = fopen ("ne2k-rxdump.txt", "wb");
+ if (!rxlog_txt) BX_PANIC (("open ne2k-rxdump.txt failed"));
+ fprintf (rxlog_txt, "tap packetmover readable log file\n");
+ fprintf (rxlog_txt, "net IF = %s\n", netif);
+ fprintf (rxlog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (rxlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (rxlog_txt, "\n--\n");
+ fflush (rxlog_txt);
+
+#endif
+}
+
+void
+bx_tap_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+ Bit8u txbuf[BX_PACKET_BUFSIZ];
+ txbuf[0] = 0;
+ txbuf[1] = 0;
+#if defined(__FreeBSD__) || defined(__APPLE__) // Should be fixed for other *BSD
+ memcpy (txbuf, buf, io_len);
+ unsigned int size = write (fd, txbuf, io_len);
+ if (size != io_len) {
+#else
+ memcpy (txbuf+2, buf, io_len);
+ unsigned int size = write (fd, txbuf, io_len+2);
+ if (size != io_len+2) {
+#endif
+ BX_PANIC (("write on tap device: %s", strerror (errno)));
+ } else {
+ BX_INFO (("wrote %d bytes + 2 byte pad on tap", io_len));
+ }
+#if BX_ETH_TAP_LOGGING
+ BX_DEBUG (("sendpkt length %u", io_len));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (buf, io_len, 1, txlog);
+ if (n != 1) BX_ERROR (("fwrite to txlog failed, io_len = %u", io_len));
+ // dump packet in hex into an ascii log file
+ fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<(int)io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (txlog_txt, "\n");
+ fprintf (txlog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (txlog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (txlog);
+ fflush (txlog_txt);
+#endif
+}
+
+void bx_tap_pktmover_c::rx_timer_handler (void *this_ptr)
+{
+ bx_tap_pktmover_c *class_ptr = (bx_tap_pktmover_c *) this_ptr;
+ class_ptr->rx_timer();
+}
+
+void bx_tap_pktmover_c::rx_timer ()
+{
+ int nbytes;
+ Bit8u buf[BX_PACKET_BUFSIZ];
+ Bit8u *rxbuf;
+ if (fd<0) return;
+ nbytes = read (fd, buf, sizeof(buf));
+
+ // hack: discard first two bytes
+#if defined(__FreeBSD__) || defined(__APPLE__) // Should be fixed for other *BSD
+ rxbuf = buf;
+#else
+ rxbuf = buf+2;
+ nbytes-=2;
+#endif
+
+ // hack: TAP device likes to create an ethernet header which has
+ // the same source and destination address FE:FD:00:00:00:00.
+ // Change the dest address to FE:FD:00:00:00:01.
+#if defined(__linux__)
+ rxbuf[5] = 1;
+#endif
+
+ if (nbytes>0)
+ BX_INFO (("tap read returned %d bytes", nbytes));
+ if (nbytes<0) {
+ if (errno != EAGAIN)
+ BX_ERROR (("tap read error: %s", strerror(errno)));
+ return;
+ }
+#if BX_ETH_TAP_LOGGING
+ if (nbytes > 0) {
+ BX_DEBUG (("receive packet length %u", nbytes));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (rxbuf, nbytes, 1, rxlog);
+ if (n != 1) BX_ERROR (("fwrite to rxlog failed, nbytes = %d", nbytes));
+ // dump packet in hex into an ascii log file
+ fprintf (rxlog_txt, "NE2K received a packet, length %u\n", nbytes);
+ for (n=0; n<nbytes; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (rxlog_txt, "\n");
+ fprintf (rxlog_txt, "%02x ", rxbuf[n]);
+ }
+ fprintf (rxlog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (rxlog);
+ fflush (rxlog_txt);
+ }
+#endif
+ BX_DEBUG(("eth_tap: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11]));
+ if (nbytes < 60) {
+ BX_INFO (("packet too short (%d), padding to 60", nbytes));
+ nbytes = 60;
+ }
+ (*rxh)(rxarg, rxbuf, nbytes);
+}
+
+#endif /* if BX_NE2K_SUPPORT */
diff --git a/tools/ioemu/iodev/eth_tuntap.cc b/tools/ioemu/iodev/eth_tuntap.cc
new file mode 100644
index 0000000000..f910fd55f1
--- /dev/null
+++ b/tools/ioemu/iodev/eth_tuntap.cc
@@ -0,0 +1,401 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: eth_tuntap.cc,v 1.9 2003/04/26 14:48:45 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// eth_tuntap.cc - TUN/TAP interface by Renzo Davoli <renzo@cs.unibo.it>
+//
+// WARNING: These instructions were written for ethertap, not TUN/TAP.
+//
+// Here's how to get this working. On the host machine:
+// $ su root
+// # /sbin/insmod ethertap
+// Using /lib/modules/2.2.14-5.0/net/ethertap.o
+// # mknod /dev/tap0 c 36 16 # if not already there
+// # /sbin/ifconfig tap0 10.0.0.1
+// # /sbin/route add -host 10.0.0.2 gw 10.0.0.1
+//
+// Now you have a tap0 device which you can on the ifconfig output. The
+// tap0 interface has the IP address of 10.0.0.1. The bochs machine will have
+// the IP address 10.0.0.2.
+//
+// Compile a bochs version from March 8, 2002 or later with --enable-ne2000.
+// Add this ne2k line to your .bochsrc to activate the tap device.
+// ne2k: ioaddr=0x280, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0
+// Don't change the mac or ethmod!
+//
+// Boot up DLX Linux in Bochs. Log in as root and then type the following
+// commands to set up networking:
+// # ifconfig eth0 10.0.0.2
+// # route add -net 10.0.0.0
+// # route add default gw 10.0.0.1
+// Now you should be able to ping from guest OS to your host machine, if
+// you give its IP number. I'm still having trouble with pings from the
+// host machine to the guest, so something is still not right. Symptoms: I
+// ping from the host to the guest's IP address 10.0.0.2. With tcpdump I can
+// see the ping going to Bochs, and then the ping reply coming from Bochs.
+// But the ping program itself does not see the responses....well every
+// once in a while it does, like 1 in 60 pings.
+//
+// host$ ping 10.0.0.2
+// PING 10.0.0.2 (10.0.0.2) from 10.0.0.1 : 56(84) bytes of data.
+//
+// Netstat output:
+// 20:29:59.018776 fe:fd:0:0:0:0 fe:fd:0:0:0:1 0800 98: 10.0.0.1 > 10.0.0.2: icmp: echo request
+// 4500 0054 2800 0000 4001 3ea7 0a00 0001
+// 0a00 0002 0800 09d3 a53e 0400 9765 893c
+// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213
+// 1415 1617 1819
+// 20:29:59.023017 fe:fd:0:0:0:1 fe:fd:0:0:0:0 0800 98: 10.0.0.2 > 10.0.0.1: icmp: echo reply
+// 4500 0054 004a 0000 4001 665d 0a00 0002
+// 0a00 0001 0000 11d3 a53e 0400 9765 893c
+// 3949 0000 0809 0a0b 0c0d 0e0f 1011 1213
+// 1415 1617 1819
+//
+// I suspect it may be related to the fact that ping 10.0.0.1 from the
+// host also doesn't work. Why wouldn't the host respond to its own IP
+// address on the tap0 device?
+//
+// Theoretically, if you set up packet forwarding (with masquerading) on the
+// host, you should be able to get Bochs talking to anyone on the internet.
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT
+
+#define LOG_THIS bx_devices.pluginNE2kDevice->
+
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <asm/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <linux/netlink.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#define TUNTAP_VIRTUAL_HW_ADDR 0xDEADBEEF
+#define BX_ETH_TUNTAP_LOGGING 0
+#define BX_PACKET_BUFSIZ 2048 // Enough for an ether frame
+
+int tun_alloc(char *dev);
+
+//
+// Define the class. This is private to this module
+//
+class bx_tuntap_pktmover_c : public eth_pktmover_c {
+public:
+ bx_tuntap_pktmover_c(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg);
+ void sendpkt(void *buf, unsigned io_len);
+private:
+ int fd;
+ int rx_timer_index;
+ static void rx_timer_handler(void *);
+ void rx_timer ();
+ FILE *txlog, *txlog_txt, *rxlog, *rxlog_txt;
+};
+
+
+//
+// Define the static class that registers the derived pktmover class,
+// and allocates one on request.
+//
+class bx_tuntap_locator_c : public eth_locator_c {
+public:
+ bx_tuntap_locator_c(void) : eth_locator_c("tuntap") {}
+protected:
+ eth_pktmover_c *allocate(const char *netif, const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg) {
+ return (new bx_tuntap_pktmover_c(netif, macaddr, rxh, rxarg));
+ }
+} bx_tuntap_match;
+
+
+//
+// Define the methods for the bx_tuntap_pktmover derived class
+//
+
+// the constructor
+bx_tuntap_pktmover_c::bx_tuntap_pktmover_c(const char *netif,
+ const char *macaddr,
+ eth_rx_handler_t rxh,
+ void *rxarg)
+{
+ int flags;
+ if (strncmp (netif, "tun", 3) != 0) {
+ BX_PANIC (("eth_tuntap: interface name (%s) must be tun", netif));
+ }
+#ifdef NEVERDEF
+ char filename[BX_PATHNAME_LEN];
+ sprintf (filename, "/dev/net/%s", netif);
+
+ // check if the TUN/TAP devices is running, and turn on ARP. This is based
+ // on code from the Mac-On-Linux project. http://http://www.maconlinux.org/
+ int sock = socket( AF_INET, SOCK_DGRAM, 0 );
+ if (sock < 0) {
+ BX_PANIC (("socket creation: %s", strerror(errno)));
+ return;
+ }
+ struct ifreq ifr;
+ memset( &ifr, 0, sizeof(ifr) );
+ strncpy( ifr.ifr_name, netif, sizeof(ifr.ifr_name) );
+ if( ioctl( sock, SIOCGIFFLAGS, &ifr ) < 0 ){
+ BX_PANIC (("SIOCGIFFLAGS on %s: %s", netif, strerror (errno)));
+ close(sock);
+ return;
+ }
+ if( !(ifr.ifr_flags & IFF_RUNNING ) ){
+ BX_PANIC (("%s device is not running", netif));
+ close(sock);
+ return;
+ }
+ if( (ifr.ifr_flags & IFF_NOARP ) ){
+ BX_INFO (("turn on ARP for %s device", netif));
+ ifr.ifr_flags &= ~IFF_NOARP;
+ if( ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0 ) {
+ BX_PANIC (("SIOCSIFFLAGS: %s", strerror(errno)));
+ close(sock);
+ return;
+ }
+ }
+ close(sock);
+
+ fd = open (filename, O_RDWR);
+#endif
+ char intname[IFNAMSIZ];
+ strcpy(intname,netif);
+ fd=tun_alloc(intname);
+ if (fd < 0) {
+ BX_PANIC (("open failed on %s: %s", netif, strerror (errno)));
+ return;
+ }
+
+ /* set O_ASYNC flag so that we can poll with read() */
+ if ((flags = fcntl( fd, F_GETFL)) < 0) {
+ BX_PANIC (("getflags on tun device: %s", strerror (errno)));
+ }
+ flags |= O_NONBLOCK;
+ if (fcntl( fd, F_SETFL, flags ) < 0) {
+ BX_PANIC (("set tun device flags: %s", strerror (errno)));
+ }
+
+ BX_INFO (("eth_tuntap: opened %s device", netif));
+
+ /* Execute the configuration script */
+ char *scriptname=bx_options.ne2k.Oscript->getptr();
+ if((scriptname != NULL)
+ &&(strcmp(scriptname, "") != 0)
+ &&(strcmp(scriptname, "none") != 0)) {
+ if (execute_script(scriptname, intname) < 0)
+ BX_ERROR (("execute script '%s' on %s failed", scriptname, intname));
+ }
+
+ // Start the rx poll
+ this->rx_timer_index =
+ bx_pc_system.register_timer(this, this->rx_timer_handler, 1000,
+ 1, 1, "eth_tuntap"); // continuous, active
+ this->rxh = rxh;
+ this->rxarg = rxarg;
+#if BX_ETH_TUNTAP_LOGGING
+ // eventually Bryce wants txlog to dump in pcap format so that
+ // tcpdump -r FILE can read it and interpret packets.
+ txlog = fopen ("ne2k-tx.log", "wb");
+ if (!txlog) BX_PANIC (("open ne2k-tx.log failed"));
+ txlog_txt = fopen ("ne2k-txdump.txt", "wb");
+ if (!txlog_txt) BX_PANIC (("open ne2k-txdump.txt failed"));
+ fprintf (txlog_txt, "tuntap packetmover readable log file\n");
+ fprintf (txlog_txt, "net IF = %s\n", netif);
+ fprintf (txlog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (txlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (txlog_txt, "\n--\n");
+ fflush (txlog_txt);
+
+ rxlog = fopen ("ne2k-rx.log", "wb");
+ if (!rxlog) BX_PANIC (("open ne2k-rx.log failed"));
+ rxlog_txt = fopen ("ne2k-rxdump.txt", "wb");
+ if (!rxlog_txt) BX_PANIC (("open ne2k-rxdump.txt failed"));
+ fprintf (rxlog_txt, "tuntap packetmover readable log file\n");
+ fprintf (rxlog_txt, "net IF = %s\n", netif);
+ fprintf (rxlog_txt, "MAC address = ");
+ for (int i=0; i<6; i++)
+ fprintf (rxlog_txt, "%02x%s", 0xff & macaddr[i], i<5?":" : "");
+ fprintf (rxlog_txt, "\n--\n");
+ fflush (rxlog_txt);
+
+#endif
+}
+
+void
+bx_tuntap_pktmover_c::sendpkt(void *buf, unsigned io_len)
+{
+#ifdef NEVERDEF
+ Bit8u txbuf[BX_PACKET_BUFSIZ];
+ txbuf[0] = 0;
+ txbuf[1] = 0;
+ memcpy (txbuf+2, buf, io_len);
+ unsigned int size = write (fd, txbuf, io_len+2);
+ if (size != io_len+2) {
+ BX_PANIC (("write on tuntap device: %s", strerror (errno)));
+ } else {
+ BX_INFO (("wrote %d bytes + 2 byte pad on tuntap", io_len));
+ }
+#endif
+ unsigned int size = write (fd, buf, io_len);
+ if (size != io_len) {
+ BX_PANIC (("write on tuntap device: %s", strerror (errno)));
+ } else {
+ BX_INFO (("wrote %d bytes on tuntap", io_len));
+ }
+#if BX_ETH_TUNTAP_LOGGING
+ BX_DEBUG (("sendpkt length %u", io_len));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (buf, io_len, 1, txlog);
+ if (n != 1) BX_ERROR (("fwrite to txlog failed", io_len));
+ // dump packet in hex into an ascii log file
+ fprintf (txlog_txt, "NE2K transmitting a packet, length %u\n", io_len);
+ Bit8u *charbuf = (Bit8u *)buf;
+ for (n=0; n<io_len; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (txlog_txt, "\n");
+ fprintf (txlog_txt, "%02x ", charbuf[n]);
+ }
+ fprintf (txlog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (txlog);
+ fflush (txlog_txt);
+#endif
+}
+
+void bx_tuntap_pktmover_c::rx_timer_handler (void *this_ptr)
+{
+ bx_tuntap_pktmover_c *class_ptr = (bx_tuntap_pktmover_c *) this_ptr;
+ class_ptr->rx_timer();
+}
+
+void bx_tuntap_pktmover_c::rx_timer ()
+{
+ int nbytes;
+ Bit8u buf[BX_PACKET_BUFSIZ];
+ Bit8u *rxbuf;
+ if (fd<0) return;
+ nbytes = read (fd, buf, sizeof(buf));
+
+#ifdef NEVERDEF
+ // hack: discard first two bytes
+ rxbuf = buf+2;
+ nbytes-=2;
+#else
+ rxbuf=buf;
+#endif
+
+ // hack: TUN/TAP device likes to create an ethernet header which has
+ // the same source and destination address FE:FD:00:00:00:00.
+ // Change the dest address to FE:FD:00:00:00:01.
+ rxbuf[5] = 1;
+
+ if (nbytes>0)
+ BX_INFO (("tuntap read returned %d bytes", nbytes));
+ if (nbytes<0) {
+ if (errno != EAGAIN)
+ BX_ERROR (("tuntap read error: %s", strerror(errno)));
+ return;
+ }
+#if BX_ETH_TUNTAP_LOGGING
+ if (nbytes > 0) {
+ BX_DEBUG (("receive packet length %u", nbytes));
+ // dump raw bytes to a file, eventually dump in pcap format so that
+ // tcpdump -r FILE can interpret them for us.
+ int n = fwrite (rxbuf, nbytes, 1, rxlog);
+ if (n != 1) BX_ERROR (("fwrite to rxlog failed", nbytes));
+ // dump packet in hex into an ascii log file
+ fprintf (rxlog_txt, "NE2K received a packet, length %u\n", nbytes);
+ for (n=0; n<nbytes; n++) {
+ if (((n % 16) == 0) && n>0)
+ fprintf (rxlog_txt, "\n");
+ fprintf (rxlog_txt, "%02x ", rxbuf[n]);
+ }
+ fprintf (rxlog_txt, "\n--\n");
+ // flush log so that we see the packets as they arrive w/o buffering
+ fflush (rxlog);
+ fflush (rxlog_txt);
+ }
+#endif
+ BX_DEBUG(("eth_tuntap: got packet: %d bytes, dst=%x:%x:%x:%x:%x:%x, src=%x:%x:%x:%x:%x:%x\n", nbytes, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4], rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9], rxbuf[10], rxbuf[11]));
+ if (nbytes < 60) {
+ BX_INFO (("packet too short (%d), padding to 60", nbytes));
+ nbytes = 60;
+ }
+ (*rxh)(rxarg, rxbuf, nbytes);
+}
+
+
+ int tun_alloc(char *dev)
+ {
+ struct ifreq ifr;
+ int fd, err;
+
+ if( (fd = open("/dev/net/tun", O_RDWR)) < 0 )
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+
+ /* Flags: IFF_TUN - TUN device (no Ethernet headers)
+ * IFF_TAP - TAP device
+ *
+ * IFF_NO_PI - Do not provide packet information
+ */
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+ if( *dev )
+ strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+ if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
+ close(fd);
+ return err;
+ }
+
+ //strcpy(dev, ifr.ifr_name);
+ ioctl( fd, TUNSETNOCSUM, 1 );
+
+ return fd;
+ }
+
+#endif /* if BX_NE2K_SUPPORT */
diff --git a/tools/ioemu/iodev/extfpuirq.cc b/tools/ioemu/iodev/extfpuirq.cc
new file mode 100644
index 0000000000..5d1ed986bf
--- /dev/null
+++ b/tools/ioemu/iodev/extfpuirq.cc
@@ -0,0 +1,107 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: extfpuirq.cc,v 1.5 2003/07/31 12:04:48 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// External circuit for MSDOS compatible FPU exceptions
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS theExternalFpuIrq->
+
+bx_extfpuirq_c *theExternalFpuIrq = NULL;
+
+ int
+libextfpuirq_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theExternalFpuIrq = new bx_extfpuirq_c ();
+ bx_devices.pluginExtFpuIrq = theExternalFpuIrq;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theExternalFpuIrq, BX_PLUGIN_EXTFPUIRQ);
+ return(0); // Success
+}
+
+ void
+libextfpuirq_LTX_plugin_fini(void)
+{
+}
+
+bx_extfpuirq_c::bx_extfpuirq_c(void)
+{
+ put("EFIRQ");
+ settype(EXTFPUIRQLOG);
+}
+
+bx_extfpuirq_c::~bx_extfpuirq_c(void)
+{
+ // nothing for now
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_extfpuirq_c::init(void)
+{
+ // called once when bochs initializes
+ DEV_register_iowrite_handler(this, write_handler, 0x00F0, "External FPU IRQ", 1);
+ DEV_register_irq(13, "External FPU IRQ");
+}
+
+ void
+bx_extfpuirq_c::reset(unsigned type)
+{
+ // We should handle IGNNE here
+ DEV_pic_lower_irq(13);
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_extfpuirq_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_EFI_SMF
+ bx_extfpuirq_c *class_ptr = (bx_extfpuirq_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_extfpuirq_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_EFI_SMF
+
+ // We should handle IGNNE here
+ DEV_pic_lower_irq(13);
+}
+
diff --git a/tools/ioemu/iodev/extfpuirq.h b/tools/ioemu/iodev/extfpuirq.h
new file mode 100644
index 0000000000..c32e71aa93
--- /dev/null
+++ b/tools/ioemu/iodev/extfpuirq.h
@@ -0,0 +1,51 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: extfpuirq.h,v 1.2 2003/01/07 08:17:15 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#if BX_USE_EFI_SMF
+# define BX_EXTFPUIRQ_SMF static
+# define BX_EXTFPUIRQ_THIS theExternalFpuIrq->
+#else
+# define BX_EXTFPUIRQ_SMF
+# define BX_EXTFPUIRQ_THIS this->
+#endif
+
+
+class bx_extfpuirq_c : public bx_devmodel_c {
+
+public:
+ bx_extfpuirq_c(void);
+ ~bx_extfpuirq_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+
+private:
+
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_EFI_SMF
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+ };
diff --git a/tools/ioemu/iodev/floppy.cc b/tools/ioemu/iodev/floppy.cc
new file mode 100644
index 0000000000..e4090d57b9
--- /dev/null
+++ b/tools/ioemu/iodev/floppy.cc
@@ -0,0 +1,1633 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: floppy.cc,v 1.69 2003/12/18 20:04:49 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+//
+// Floppy Disk Controller Docs:
+// Intel 82077A Data sheet
+// ftp://void-core.2y.net/pub/docs/fdc/82077AA_FloppyControllerDatasheet.pdf
+// Intel 82078 Data sheet
+// ftp://download.intel.com/design/periphrl/datashts/29047403.PDF
+// Other FDC references
+// http://debs.future.easyspace.com/Programming/Hardware/FDC/floppy.html
+// And a port list:
+// http://mudlist.eorbit.net/~adam/pickey/ports.html
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+
+extern "C" {
+#include <errno.h>
+}
+
+#ifdef __linux__
+extern "C" {
+#include <sys/ioctl.h>
+#include <linux/fd.h>
+}
+#endif
+#include "bochs.h"
+// windows.h included by bochs.h
+#ifdef WIN32
+extern "C" {
+#include <winioctl.h>
+}
+#endif
+#define LOG_THIS theFloppyController->
+
+bx_floppy_ctrl_c *theFloppyController;
+
+/* for main status register */
+#define FD_MS_MRQ 0x80
+#define FD_MS_DIO 0x40
+#define FD_MS_NDMA 0x20
+#define FD_MS_BUSY 0x10
+#define FD_MS_ACTD 0x08
+#define FD_MS_ACTC 0x04
+#define FD_MS_ACTB 0x02
+#define FD_MS_ACTA 0x01
+
+#define FROM_FLOPPY 10
+#define TO_FLOPPY 11
+
+#define FLOPPY_DMA_CHAN 2
+
+typedef struct {
+ unsigned id;
+ Bit8u trk;
+ Bit8u hd;
+ Bit8u spt;
+ unsigned sectors;
+} floppy_type_t;
+
+static floppy_type_t floppy_type[8] = {
+ {BX_FLOPPY_160K, 40, 1, 8, 320},
+ {BX_FLOPPY_180K, 40, 1, 9, 360},
+ {BX_FLOPPY_320K, 40, 2, 8, 640},
+ {BX_FLOPPY_360K, 40, 2, 9, 720},
+ {BX_FLOPPY_720K, 80, 2, 9, 1440},
+ {BX_FLOPPY_1_2, 80, 2, 15, 2400},
+ {BX_FLOPPY_1_44, 80, 2, 18, 2880},
+ {BX_FLOPPY_2_88, 80, 2, 36, 5760}
+};
+
+
+ int
+libfloppy_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theFloppyController = new bx_floppy_ctrl_c ();
+ bx_devices.pluginFloppyDevice = theFloppyController;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theFloppyController, BX_PLUGIN_FLOPPY);
+ return(0); // Success
+}
+
+ void
+libfloppy_LTX_plugin_fini(void)
+{
+}
+
+
+bx_floppy_ctrl_c::bx_floppy_ctrl_c(void)
+{
+ put("FDD");
+ settype(FDLOG);
+ s.floppy_timer_index = BX_NULL_TIMER_HANDLE;
+}
+
+bx_floppy_ctrl_c::~bx_floppy_ctrl_c(void)
+{
+ // nothing for now
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_floppy_ctrl_c::init(void)
+{
+ Bit8u i;
+
+ BX_DEBUG(("Init $Id: floppy.cc,v 1.69 2003/12/18 20:04:49 vruppert Exp $"));
+ DEV_dma_register_8bit_channel(2, dma_read, dma_write, "Floppy Drive");
+ DEV_register_irq(6, "Floppy Drive");
+ for (unsigned addr=0x03F2; addr<=0x03F7; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "Floppy Drive", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "Floppy Drive", 1);
+ }
+
+
+ DEV_cmos_set_reg(0x10, 0x00); /* start out with: no drive 0, no drive 1 */
+
+ BX_FD_THIS s.num_supported_floppies = 0;
+
+ for (i=0; i<4; i++) {
+ BX_FD_THIS s.device_type[i] = BX_FLOPPY_NONE;
+ BX_FD_THIS s.media[i].type = BX_FLOPPY_NONE;
+ }
+
+ //
+ // Floppy A setup
+ //
+ BX_FD_THIS s.media[0].sectors_per_track = 0;
+ BX_FD_THIS s.media[0].tracks = 0;
+ BX_FD_THIS s.media[0].heads = 0;
+ BX_FD_THIS s.media[0].sectors = 0;
+ BX_FD_THIS s.media[0].fd = -1;
+ BX_FD_THIS s.media_present[0] = 0;
+ BX_FD_THIS s.device_type[0] = bx_options.floppya.Odevtype->get ();
+
+ switch (BX_FD_THIS s.device_type[0]) {
+ case BX_FLOPPY_NONE:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x00);
+ break;
+ case BX_FLOPPY_360K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x10);
+ break;
+ case BX_FLOPPY_1_2:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x20);
+ break;
+ case BX_FLOPPY_720K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x30);
+ break;
+ case BX_FLOPPY_1_44:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x40);
+ break;
+ case BX_FLOPPY_2_88:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x50);
+ break;
+
+ // use CMOS reserved types
+ case BX_FLOPPY_160K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x60);
+ BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 6"));
+ break;
+ case BX_FLOPPY_180K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x70);
+ BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 7"));
+ break;
+ case BX_FLOPPY_320K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0x0f) | 0x80);
+ BX_INFO(("WARNING: 1st floppy uses of reserved CMOS floppy drive type 8"));
+ break;
+
+ default:
+ BX_PANIC(("unknown floppya type"));
+ }
+ if (BX_FD_THIS s.device_type[0] != BX_FLOPPY_NONE)
+ BX_FD_THIS s.num_supported_floppies++;
+
+ if (bx_options.floppya.Otype->get () != BX_FLOPPY_NONE) {
+ if ( bx_options.floppya.Ostatus->get () == BX_INSERTED) {
+ if (evaluate_media(bx_options.floppya.Otype->get (), bx_options.floppya.Opath->getptr (),
+ & BX_FD_THIS s.media[0]))
+ BX_FD_THIS s.media_present[0] = 1;
+ else
+ bx_options.floppya.Ostatus->set(BX_EJECTED);
+#define MED (BX_FD_THIS s.media[0])
+ BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppya.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ }
+ }
+
+
+ //
+ // Floppy B setup
+ //
+ BX_FD_THIS s.media[1].sectors_per_track = 0;
+ BX_FD_THIS s.media[1].tracks = 0;
+ BX_FD_THIS s.media[1].heads = 0;
+ BX_FD_THIS s.media[1].sectors = 0;
+ BX_FD_THIS s.media[1].fd = -1;
+ BX_FD_THIS s.media_present[1] = 0;
+ BX_FD_THIS s.device_type[1] = bx_options.floppyb.Odevtype->get ();
+
+ switch (BX_FD_THIS s.device_type[1]) {
+ case BX_FLOPPY_NONE:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x00);
+ break;
+ case BX_FLOPPY_360K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x01);
+ break;
+ case BX_FLOPPY_1_2:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x02);
+ break;
+ case BX_FLOPPY_720K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x03);
+ break;
+ case BX_FLOPPY_1_44:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x04);
+ break;
+ case BX_FLOPPY_2_88:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x05);
+ break;
+
+ // use CMOS reserved types
+ case BX_FLOPPY_160K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x06);
+ BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 6"));
+ break;
+ case BX_FLOPPY_180K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x07);
+ BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 7"));
+ break;
+ case BX_FLOPPY_320K:
+ DEV_cmos_set_reg(0x10, (DEV_cmos_get_reg(0x10) & 0xf0) | 0x08);
+ BX_INFO(("WARNING: 2nd floppy uses of reserved CMOS floppy drive type 8"));
+ break;
+
+ default:
+ BX_PANIC(("unknown floppyb type"));
+ }
+ if (BX_FD_THIS s.device_type[1] != BX_FLOPPY_NONE)
+ BX_FD_THIS s.num_supported_floppies++;
+
+ if (bx_options.floppyb.Otype->get () != BX_FLOPPY_NONE) {
+ if ( bx_options.floppyb.Ostatus->get () == BX_INSERTED) {
+ if (evaluate_media(bx_options.floppyb.Otype->get (), bx_options.floppyb.Opath->getptr (),
+ & BX_FD_THIS s.media[1]))
+ BX_FD_THIS s.media_present[1] = 1;
+ else
+ bx_options.floppyb.Ostatus->set(BX_EJECTED);
+#define MED (BX_FD_THIS s.media[1])
+ BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppyb.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ }
+ }
+
+
+
+ /* CMOS Equipment Byte register */
+ if (BX_FD_THIS s.num_supported_floppies > 0) {
+ DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e) |
+ ((BX_FD_THIS s.num_supported_floppies-1) << 6) | 1);
+ }
+ else
+ DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0x3e));
+
+
+ if (BX_FD_THIS s.floppy_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_FD_THIS s.floppy_timer_index =
+ bx_pc_system.register_timer( this, timer_handler,
+ bx_options.Ofloppy_command_delay->get (), 0,0, "floppy");
+ }
+
+ BX_DEBUG(("bx_options.Ofloppy_command_delay = %u",
+ (unsigned) bx_options.Ofloppy_command_delay->get ()));
+}
+
+
+
+ void
+bx_floppy_ctrl_c::reset(unsigned type)
+{
+ Bit32u i;
+
+ BX_FD_THIS s.pending_irq = 0;
+ BX_FD_THIS s.reset_sensei = 0; /* no reset result present */
+
+ BX_FD_THIS s.main_status_reg = 0;
+ BX_FD_THIS s.status_reg0 = 0;
+ BX_FD_THIS s.status_reg1 = 0;
+ BX_FD_THIS s.status_reg2 = 0;
+ BX_FD_THIS s.status_reg3 = 0;
+
+ // software reset (via DOR port 0x3f2 bit 2) does not change DOR
+ if (type == BX_RESET_HARDWARE) {
+ BX_FD_THIS s.DOR = 0x0c;
+ // motor off, drive 3..0
+ // DMA/INT enabled
+ // normal operation
+ // drive select 0
+
+ // DIR and CCR affected only by hard reset
+ for (i=0; i<4; i++) {
+ BX_FD_THIS s.DIR[i] |= 0x80; // disk changed
+ }
+ BX_FD_THIS s.data_rate = 0; /* 500 Kbps */
+ }
+
+ for (i=0; i<4; i++) {
+ BX_FD_THIS s.cylinder[i] = 0;
+ BX_FD_THIS s.head[i] = 0;
+ BX_FD_THIS s.sector[i] = 0;
+ }
+
+ DEV_pic_lower_irq(6);
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_idle_phase();
+}
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_floppy_ctrl_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_FD_SMF
+ bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ /* reads from the floppy io ports */
+ Bit32u
+bx_floppy_ctrl_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_FD_SMF
+ Bit8u status, value;
+
+ if (bx_dbg.floppy)
+ BX_INFO(("read access to port %04x", (unsigned) address));
+
+ switch (address) {
+#if BX_DMA_FLOPPY_IO
+ case 0x3F2: // diskette controller digital output register
+ value = BX_FD_THIS s.DOR;
+ return(value);
+ break;
+
+ case 0x3F4: /* diskette controller main status register */
+ status = BX_FD_THIS s.main_status_reg;
+ return(status);
+ break;
+
+ case 0x3F5: /* diskette controller data */
+ if (BX_FD_THIS s.result_size == 0) {
+ BX_ERROR(("port 0x3f5: no results to read"));
+ BX_FD_THIS s.main_status_reg = 0;
+ return BX_FD_THIS s.result[0];
+ }
+
+ value = BX_FD_THIS s.result[BX_FD_THIS s.result_index++];
+ BX_FD_THIS s.main_status_reg &= 0xF0;
+ if (BX_FD_THIS s.result_index >= BX_FD_THIS s.result_size) {
+ if (!BX_FD_THIS s.reset_sensei) BX_FD_THIS s.pending_irq = 0;
+ DEV_pic_lower_irq(6);
+ enter_idle_phase();
+ }
+ return(value);
+ break;
+#endif // #if BX_DMA_FLOPPY_IO
+
+ case 0x3F3: // Tape Drive Register
+ // see http://www.smsc.com/main/datasheets/37c93x.pdf page 18 for more details
+
+ switch( BX_FD_THIS s.DOR & 0x03 )
+ {
+ case 0x00:
+ if( (BX_FD_THIS s.DOR & 0x10) == 0) break;
+ return(2);
+ case 0x01:
+ if( (BX_FD_THIS s.DOR & 0x20) == 0) break;
+ return(1);
+ }
+ return(3);
+
+ case 0x3F6: // Reserved for future floppy controllers
+ // This address shared with the hard drive controller
+ value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len);
+ return( value );
+ break;
+
+ case 0x3F7: // diskette controller digital input register
+ // This address shared with the hard drive controller:
+ // Bit 7 : floppy
+ // Bits 6..0: hard drive
+ value = DEV_hd_read_handler(bx_devices.pluginHardDrive, address, io_len);
+ value &= 0x7f;
+ // add in diskette change line
+ value |= (BX_FD_THIS s.DIR[BX_FD_THIS s.DOR & 0x03] & 0x80);
+ return( value );
+ break;
+ default:
+ BX_ERROR(("io_read: unsupported address 0x%04x", (unsigned) address));
+ return(0);
+ break;
+ }
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_floppy_ctrl_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_FD_SMF
+ bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ /* writes to the floppy io ports */
+ void
+bx_floppy_ctrl_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_FD_SMF
+ Bit8u dma_and_interrupt_enable;
+ Bit8u normal_operation, prev_normal_operation;
+ Bit8u drive_select;
+ Bit8u motor_on_drive0, motor_on_drive1;
+
+ if (bx_dbg.floppy)
+ BX_INFO(("write access to port %04x, value=%02x",
+ (unsigned) address, (unsigned) value));
+
+ switch (address) {
+#if BX_DMA_FLOPPY_IO
+ case 0x3F2: /* diskette controller digital output register */
+ motor_on_drive1 = value & 0x20;
+ motor_on_drive0 = value & 0x10;
+ dma_and_interrupt_enable = value & 0x08;
+ if (!dma_and_interrupt_enable)
+ BX_DEBUG(("DMA and interrupt capabilities disabled"));
+ normal_operation = value & 0x04;
+ drive_select = value & 0x03;
+
+ prev_normal_operation = BX_FD_THIS s.DOR & 0x04;
+ BX_FD_THIS s.DOR = value;
+
+ if (prev_normal_operation==0 && normal_operation) {
+ // transition from RESET to NORMAL
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ }
+ else if (prev_normal_operation && normal_operation==0) {
+ // transition from NORMAL to RESET
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ BX_FD_THIS s.pending_command = 0xfe; // RESET pending
+
+ }
+ BX_DEBUG(("io_write: digital output register"));
+ BX_DEBUG((" motor on, drive1 = %d", motor_on_drive1 > 0));
+ BX_DEBUG((" motor on, drive0 = %d", motor_on_drive0 > 0));
+ BX_DEBUG((" dma_and_interrupt_enable=%02x",
+ (unsigned) dma_and_interrupt_enable));
+ BX_DEBUG((" normal_operation=%02x",
+ (unsigned) normal_operation));
+ BX_DEBUG((" drive_select=%02x",
+ (unsigned) drive_select));
+ if (BX_FD_THIS s.device_type[drive_select] == BX_FLOPPY_NONE) {
+ BX_DEBUG(("WARNING: not existing drive selected"));
+ }
+ break;
+
+ case 0x3f4: /* diskette controller data rate select register */
+ BX_ERROR(("io_write: data rate select register unsupported"));
+ break;
+
+ case 0x3F5: /* diskette controller data */
+ BX_DEBUG(("command = %02x", (unsigned) value));
+ if (BX_FD_THIS s.command_complete) {
+ if (BX_FD_THIS s.pending_command!=0)
+ BX_PANIC(("io: 3f5: receiving new comm, old one (%02x) pending",
+ (unsigned) BX_FD_THIS s.pending_command));
+ BX_FD_THIS s.command[0] = value;
+ BX_FD_THIS s.command_complete = 0;
+ BX_FD_THIS s.command_index = 1;
+ /* read/write command in progress */
+ BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_BUSY;
+ switch (value) {
+ case 0x03: /* specify */
+ BX_FD_THIS s.command_size = 3;
+ break;
+ case 0x04: // get status
+ BX_FD_THIS s.command_size = 2;
+ break;
+ case 0x07: /* recalibrate */
+ BX_FD_THIS s.command_size = 2;
+ break;
+ case 0x08: /* sense interrupt status */
+ BX_FD_THIS s.command_size = 1;
+ break;
+ case 0x0f: /* seek */
+ BX_FD_THIS s.command_size = 3;
+ break;
+ case 0x4a: /* read ID */
+ BX_FD_THIS s.command_size = 2;
+ break;
+ case 0x4d: /* format track */
+ BX_FD_THIS s.command_size = 6;
+ break;
+ case 0x45:
+ case 0xc5: /* write normal data */
+ BX_FD_THIS s.command_size = 9;
+ break;
+ case 0x46:
+ case 0x66:
+ case 0xc6:
+ case 0xe6: /* read normal data */
+ BX_FD_THIS s.command_size = 9;
+ break;
+
+ case 0x13: // Configure command (Enhanced)
+ BX_FD_THIS s.command_size = 4;
+ break;
+
+ case 0x0e: // dump registers (Enhanced drives)
+ case 0x10: // Version command, standard controller returns 80h
+ case 0x18: // National Semiconductor version command; return 80h
+ // These commands are not implemented on the standard
+ // controller and return an error. They are available on
+ // the enhanced controller.
+ BX_DEBUG(("io_write: 0x3f5: unsupported floppy command 0x%02x",
+ (unsigned) value));
+ BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command
+ BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command
+ enter_result_phase();
+ break;
+
+ default:
+ BX_ERROR(("io_write: 0x3f5: invalid floppy command 0x%02x",
+ (unsigned) value));
+ BX_FD_THIS s.command_size = 0; // make sure we don't try to process this command
+ BX_FD_THIS s.status_reg0 = 0x80; // status: invalid command
+ enter_result_phase();
+ break;
+ }
+ }
+ else {
+ BX_FD_THIS s.command[BX_FD_THIS s.command_index++] =
+ value;
+ }
+ if (BX_FD_THIS s.command_index ==
+ BX_FD_THIS s.command_size) {
+ /* read/write command not in progress any more */
+ floppy_command();
+ BX_FD_THIS s.command_complete = 1;
+ }
+ BX_DEBUG(("io_write: diskette controller data"));
+ return;
+ break;
+#endif // #if BX_DMA_FLOPPY_IO
+
+ case 0x3F6: /* diskette controller (reserved) */
+ BX_DEBUG(("io_write: reserved register 0x3f6 unsupported"));
+ // this address shared with the hard drive controller
+ DEV_hd_write_handler(bx_devices.pluginHardDrive, address, value, io_len);
+ break;
+
+#if BX_DMA_FLOPPY_IO
+ case 0x3F7: /* diskette controller configuration control register */
+ BX_DEBUG(("io_write: config control register"));
+ BX_FD_THIS s.data_rate = value & 0x03;
+ switch (BX_FD_THIS s.data_rate) {
+ case 0: BX_DEBUG((" 500 Kbps")); break;
+ case 1: BX_DEBUG((" 300 Kbps")); break;
+ case 2: BX_DEBUG((" 250 Kbps")); break;
+ case 3: BX_DEBUG((" 1 Mbps")); break;
+ }
+ return;
+ break;
+
+ default:
+ BX_ERROR(("io_write ignored: 0x%04x = 0x%02x", (unsigned) address, (unsigned) value));
+ break;
+#endif // #if BX_DMA_FLOPPY_IO
+ }
+}
+
+
+
+ void
+bx_floppy_ctrl_c::floppy_command(void)
+{
+#if BX_PROVIDE_CPU_MEMORY==0
+ BX_PANIC(("floppy_command(): uses DMA: not supported for"
+ " external environment"));
+#else
+ unsigned i;
+ Bit8u step_rate_time;
+ Bit8u head_unload_time;
+ Bit8u head_load_time;
+ Bit8u motor_on;
+ Bit8u head, drive, cylinder, sector, eot;
+ Bit8u sector_size, data_length;
+ Bit32u logical_sector;
+
+
+ BX_DEBUG(("FLOPPY COMMAND: "));
+ for (i=0; i<BX_FD_THIS s.command_size; i++)
+ BX_DEBUG(("[%02x] ", (unsigned) BX_FD_THIS s.command[i]));
+
+#if 0
+ /* execute phase of command is in progress (non DMA mode) */
+ BX_FD_THIS s.main_status_reg |= 20;
+#endif
+
+ BX_FD_THIS s.pending_command = BX_FD_THIS s.command[0];
+ switch (BX_FD_THIS s.pending_command) {
+ case 0x03: // specify
+ // execution: specified parameters are loaded
+ // result: no result bytes, no interrupt
+ step_rate_time = BX_FD_THIS s.command[1] >> 4;
+ head_unload_time = BX_FD_THIS s.command[1] & 0x0f;
+ head_load_time = BX_FD_THIS s.command[2] >> 1;
+ if (BX_FD_THIS s.command[2] & 0x01)
+ BX_ERROR(("non DMA mode selected"));
+ enter_idle_phase();
+ return;
+ break;
+
+ case 0x04: // get status
+ drive = (BX_FD_THIS s.command[1] & 0x03);
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ BX_FD_THIS s.status_reg3 = 0x28 | (BX_FD_THIS s.head[drive]<<2) | drive
+ | (BX_FD_THIS s.media[drive].write_protected ? 0x40 : 0x00);
+ if (BX_FD_THIS s.cylinder[drive] == 0) BX_FD_THIS s.status_reg3 |= 0x10;
+ enter_result_phase();
+ return;
+ break;
+
+ case 0x07: // recalibrate
+ drive = (BX_FD_THIS s.command[1] & 0x03);
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+ BX_DEBUG(("floppy_command(): recalibrate drive %u",
+ (unsigned) drive));
+ motor_on = ( (BX_FD_THIS s.DOR>>(drive+4))
+ & 0x01 );
+ if (motor_on == 0) {
+ BX_INFO(("floppy_command(): recal drive with motor off"));
+ }
+ if (drive==0)
+ BX_FD_THIS s.DOR |= 0x10; // turn on MOTA
+ else
+ BX_FD_THIS s.DOR |= 0x20; // turn on MOTB
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ /* command head to track 0
+ * controller set to non-busy
+ * error condition noted in Status reg 0's equipment check bit
+ * seek end bit set to 1 in Status reg 0 regardless of outcome
+ * The last two are taken care of in timer().
+ */
+ BX_FD_THIS s.cylinder[drive] = 0;
+ BX_FD_THIS s.main_status_reg = (1 << drive);
+ return;
+ break;
+
+ case 0x08: /* sense interrupt status */
+ /* execution:
+ * get status
+ * result:
+ * no interupt
+ * byte0 = status reg0
+ * byte1 = current cylinder number (0 to 79)
+ */
+ drive = BX_FD_THIS s.DOR & 0x03;
+ if (!BX_FD_THIS s.pending_irq) {
+ BX_FD_THIS s.status_reg0 = 0x80;
+ }
+ else {
+ if (BX_FD_THIS s.reset_sensei > 0) {
+ drive = 4 - BX_FD_THIS s.reset_sensei;
+ BX_FD_THIS s.status_reg0 &= 0xf8;
+ BX_FD_THIS s.status_reg0 |= (BX_FD_THIS s.head[drive] << 2) | drive;
+ BX_FD_THIS s.reset_sensei--;
+ }
+ }
+
+ BX_DEBUG(("sense interrupt status"));
+ enter_result_phase();
+ return;
+ break;
+
+ case 0x0f: /* seek */
+ /* command:
+ * byte0 = 0F
+ * byte1 = drive & head select
+ * byte2 = cylinder number
+ * execution:
+ * postion head over specified cylinder
+ * result:
+ * no result bytes, issues an interrupt
+ */
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.command[2];
+ /* ??? should also check cylinder validity */
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ /* data reg not ready, drive busy */
+ BX_FD_THIS s.main_status_reg = (1 << drive);
+ return;
+ break;
+
+ case 0x13: // Configure
+ BX_DEBUG(("configure (eis = 0x%02x)", BX_FD_THIS s.command[2] & 0x40 ));
+ BX_DEBUG(("configure (efifo = 0x%02x)", BX_FD_THIS s.command[2] & 0x20 ));
+ BX_DEBUG(("configure (no poll = 0x%02x)", BX_FD_THIS s.command[2] & 0x10 ));
+ BX_DEBUG(("configure (fifothr = 0x%02x)", BX_FD_THIS s.command[2] & 0x0f ));
+ BX_DEBUG(("configure (pretrk = 0x%02x)", BX_FD_THIS s.command[3] ));
+ enter_idle_phase();
+ return;
+ break;
+
+ case 0x4a: // read ID
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
+ if (motor_on == 0) {
+ BX_ERROR(("floppy_command(): 0x4a: motor not on"));
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ }
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_command(): read ID: bad drive #%d", drive));
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive]<<2) | drive;
+ bx_pc_system.activate_timer( BX_FD_THIS s.floppy_timer_index,
+ bx_options.Ofloppy_command_delay->get (), 0 );
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ break;
+
+ case 0x4d: // format track
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
+ if (motor_on == 0)
+ BX_PANIC(("floppy_command(): format track: motor not on"));
+ BX_FD_THIS s.head[drive] = (BX_FD_THIS s.command[1] >> 2) & 0x01;
+ sector_size = BX_FD_THIS s.command[2];
+ BX_FD_THIS s.format_count = BX_FD_THIS s.command[3];
+ BX_FD_THIS s.format_fillbyte = BX_FD_THIS s.command[5];
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_command(): format track: bad drive #%d", drive));
+
+ if (sector_size != 0x02) { // 512 bytes
+ BX_PANIC(("format track: sector size %d not supported", 128<<sector_size));
+ }
+ if (BX_FD_THIS s.format_count != BX_FD_THIS s.media[drive].sectors_per_track) {
+ BX_PANIC(("format track: %d sectors/track requested (%d expected)",
+ BX_FD_THIS s.format_count, BX_FD_THIS s.media[drive].sectors_per_track));
+ }
+ if ( BX_FD_THIS s.media_present[drive] == 0 ) {
+ // media not in drive, return error
+ BX_INFO(("attempt to format track with media not present"));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x25; // 0010 0101
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+ if (BX_FD_THIS s.media[drive].write_protected) {
+ // media write-protected, return error
+ BX_INFO(("attempt to format track with media write-protected"));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+
+ /* 4 header bytes per sector are required */
+ BX_FD_THIS s.format_count <<= 2;
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
+
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ BX_DEBUG(("format track"));
+ return;
+ break;
+
+ case 0x46: // read normal data, MT=0, SK=0
+ case 0x66: // read normal data, MT=0, SK=1
+ case 0xc6: // read normal data, MT=1, SK=0
+ case 0xe6: // read normal data, MT=1, SK=1
+ case 0x45: // write normal data, MT=0
+ case 0xc5: // write normal data, MT=1
+ BX_FD_THIS s.multi_track = (BX_FD_THIS s.command[0] >> 7);
+ if ( (BX_FD_THIS s.DOR & 0x08) == 0 )
+ BX_PANIC(("read/write command with DMA and int disabled"));
+ drive = BX_FD_THIS s.command[1] & 0x03;
+ BX_FD_THIS s.DOR &= 0xfc;
+ BX_FD_THIS s.DOR |= drive;
+
+ motor_on = (BX_FD_THIS s.DOR>>(drive+4)) & 0x01;
+ if (motor_on == 0)
+ BX_PANIC(("floppy_command(): read/write: motor not on"));
+ head = BX_FD_THIS s.command[3] & 0x01;
+ cylinder = BX_FD_THIS s.command[2]; /* 0..79 depending */
+ sector = BX_FD_THIS s.command[4]; /* 1..36 depending */
+ eot = BX_FD_THIS s.command[6]; /* 1..36 depending */
+ sector_size = BX_FD_THIS s.command[5];
+ data_length = BX_FD_THIS s.command[8];
+ BX_DEBUG(("read/write normal data"));
+ BX_DEBUG(("BEFORE"));
+ BX_DEBUG((" drive = %u", (unsigned) drive));
+ BX_DEBUG((" head = %u", (unsigned) head));
+ BX_DEBUG((" cylinder = %u", (unsigned) cylinder));
+ BX_DEBUG((" sector = %u", (unsigned) sector));
+ BX_DEBUG((" eot = %u", (unsigned) eot));
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_command(): read/write: bad drive #%d", drive));
+
+ // check that head number in command[1] bit two matches the head
+ // reported in the head number field. Real floppy drives are
+ // picky about this, as reported in SF bug #439945, (Floppy drive
+ // read input error checking).
+ if (head != ((BX_FD_THIS s.command[1]>>2)&1)) {
+ BX_ERROR(("head number in command[1] doesn't match head field"));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x04; // 0000 0100
+ BX_FD_THIS s.status_reg2 = 0x00; // 0000 0000
+ enter_result_phase();
+ return;
+ }
+
+ if ( BX_FD_THIS s.media_present[drive] == 0 ) {
+ // media not in drive, return error
+
+ BX_INFO(("attempt to read/write sector %u,"
+ " sectors/track=%u with media not present",
+ (unsigned) sector,
+ (unsigned) BX_FD_THIS s.media[drive].sectors_per_track));
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive; // abnormal termination
+ BX_FD_THIS s.status_reg1 = 0x25; // 0010 0101
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+
+ if (sector_size != 0x02) { // 512 bytes
+ BX_PANIC(("read/write command: sector size %d not supported", 128<<sector_size));
+ }
+ if ( cylinder >= BX_FD_THIS s.media[drive].tracks ) {
+ BX_PANIC(("io: norm r/w parms out of range: sec#%02xh cyl#%02xh eot#%02xh head#%02xh",
+ (unsigned) sector, (unsigned) cylinder, (unsigned) eot,
+ (unsigned) head));
+ return;
+ }
+
+ if (sector > BX_FD_THIS s.media[drive].sectors_per_track) {
+ // requested sector > last sector on track
+ BX_INFO(("attempt to read/write sector %u,"
+ " sectors/track=%u", (unsigned) sector,
+ (unsigned) BX_FD_THIS s.media[drive].sectors_per_track));
+ // set controller to where drive would have left off
+ // after it discovered the sector was past EOT
+ BX_FD_THIS s.cylinder[drive] = cylinder;
+ BX_FD_THIS s.head[drive] = head;
+ BX_FD_THIS s.sector[drive] = BX_FD_THIS s.media[drive].sectors_per_track;
+
+ // 0100 0HDD abnormal termination
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive;
+ // 1000 0101 end of cyl/NDAT/NID
+ BX_FD_THIS s.status_reg1 = 0x85;
+ // 0000 0000
+ BX_FD_THIS s.status_reg2 = 0x00;
+ enter_result_phase();
+ return;
+ }
+
+ if (cylinder != BX_FD_THIS s.cylinder[drive])
+ BX_DEBUG(("io: cylinder request != current cylinder"));
+
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (cylinder * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (head * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (sector - 1);
+
+ if (logical_sector >= BX_FD_THIS s.media[drive].sectors) {
+ BX_PANIC(("io: logical sector out of bounds"));
+ }
+
+ BX_FD_THIS s.cylinder[drive] = cylinder;
+ BX_FD_THIS s.sector[drive] = sector;
+ BX_FD_THIS s.head[drive] = head;
+
+ if ((BX_FD_THIS s.command[0] & 0x4f) == 0x46) { // read
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, FROM_FLOPPY);
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
+
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ }
+ else if ((BX_FD_THIS s.command[0] & 0x7f) == 0x45) { // write
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 1);
+
+ /* data reg not ready, controller busy */
+ BX_FD_THIS s.main_status_reg = FD_MS_BUSY;
+ return;
+ }
+ else
+ BX_PANIC(("floppy_command(): unknown read/write command"));
+
+ return;
+ break;
+
+ default: // invalid or unsupported command; these are captured in write() above
+ BX_PANIC(("You should never get here! cmd = 0x%02x",
+ BX_FD_THIS s.command[0]));
+ }
+#endif
+}
+
+ void
+bx_floppy_ctrl_c::floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer,
+ Bit32u bytes, Bit8u direction)
+{
+ int ret;
+
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE)
+ BX_PANIC(("floppy_xfer: bad drive #%d", drive));
+
+ if (bx_dbg.floppy) {
+ BX_INFO(("drive=%u", (unsigned) drive));
+ BX_INFO(("offset=%u", (unsigned) offset));
+ BX_INFO(("bytes=%u", (unsigned) bytes));
+ BX_INFO(("direction=%s", (direction==FROM_FLOPPY)? "from" : "to"));
+ }
+
+#if BX_WITH_MACOS
+ if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+#endif
+ {
+ ret = lseek(BX_FD_THIS s.media[drive].fd, offset, SEEK_SET);
+ if (ret < 0) {
+ BX_PANIC(("could not perform lseek() on floppy image file"));
+ }
+ }
+
+ if (direction == FROM_FLOPPY) {
+#if BX_WITH_MACOS
+ if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+ ret = fd_read((char *) buffer, offset, bytes);
+ else
+#endif
+ ret = ::read(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes);
+ if (ret < int(bytes)) {
+ /* ??? */
+ if (ret > 0) {
+ BX_INFO(("partial read() on floppy image returns %u/%u",
+ (unsigned) ret, (unsigned) bytes));
+ memset(buffer + ret, 0, bytes - ret);
+ }
+ else {
+ BX_INFO(("read() on floppy image returns 0"));
+ memset(buffer, 0, bytes);
+ }
+ }
+ }
+
+ else { // TO_FLOPPY
+ BX_ASSERT (!BX_FD_THIS s.media[drive].write_protected);
+#if BX_WITH_MACOS
+ if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+ ret = fd_write((char *) buffer, offset, bytes);
+ else
+#endif
+ ret = ::write(BX_FD_THIS s.media[drive].fd, (bx_ptr_t) buffer, bytes);
+ if (ret < int(bytes)) {
+ BX_PANIC(("could not perform write() on floppy image file"));
+ }
+ }
+}
+
+
+
+ void
+bx_floppy_ctrl_c::timer_handler(void *this_ptr)
+{
+
+ bx_floppy_ctrl_c *class_ptr = (bx_floppy_ctrl_c *) this_ptr;
+
+ class_ptr->timer();
+}
+
+ void
+bx_floppy_ctrl_c::timer()
+{
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+ switch ( BX_FD_THIS s.pending_command ) {
+ case 0x07: // recal
+ case 0x0f: // seek
+ BX_FD_THIS s.status_reg0 = 0x20 | (BX_FD_THIS s.head[drive]<<2) | drive;
+ if (BX_FD_THIS s.device_type[drive] == BX_FLOPPY_NONE) {
+ BX_FD_THIS s.status_reg0 |= 0x50;
+ }
+ else if (BX_FD_THIS s.media_present[drive] == 0) {
+ BX_FD_THIS s.status_reg0 |= 0x40;
+ BX_FD_THIS s.status_reg1 = 0x25;
+ BX_FD_THIS s.status_reg2 = 0x31;
+ }
+
+ /* reset changeline */
+ if (drive > 1) return;
+ if (BX_FD_THIS s.media_present[drive])
+ BX_FD_THIS s.DIR[drive] &= ~0x80; // clear disk change line
+
+ enter_idle_phase();
+ raise_interrupt();
+ break;
+
+ case 0x4a: /* read ID */
+ enter_result_phase();
+ break;
+
+ case 0xfe: // (contrived) RESET
+ theFloppyController->reset(BX_RESET_SOFTWARE);
+ BX_FD_THIS s.pending_command = 0;
+ BX_FD_THIS s.status_reg0 = 0xc0;
+ raise_interrupt();
+ BX_FD_THIS s.reset_sensei = 4;
+ break;
+
+ case 0x00: // nothing pending?
+ break;
+
+ default:
+ BX_PANIC(("floppy:timer(): unknown case %02x",
+ (unsigned) BX_FD_THIS s.pending_command));
+ }
+ return;
+}
+
+ void
+bx_floppy_ctrl_c::dma_write(Bit8u *data_byte)
+{
+ // A DMA write is from I/O to Memory
+ // We need to return then next data byte from the floppy buffer
+ // to be transfered via the DMA to memory. (read block from floppy)
+
+
+ *data_byte = BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++];
+
+ if (BX_FD_THIS s.floppy_buffer_index >= 512) {
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+ increment_sector(); // increment to next sector before retrieving next one
+ BX_FD_THIS s.floppy_buffer_index = 0;
+ if (DEV_dma_get_tc()) { // Terminal Count line, done
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
+ BX_FD_THIS s.status_reg1 = 0;
+ BX_FD_THIS s.status_reg2 = 0;
+
+ if (bx_dbg.floppy) {
+ BX_INFO(("<<READ DONE>>"));
+ BX_INFO(("AFTER"));
+ BX_INFO((" drive = %u", (unsigned) drive));
+ BX_INFO((" head = %u", (unsigned) BX_FD_THIS s.head[drive]));
+ BX_INFO((" cylinder = %u", (unsigned) BX_FD_THIS s.cylinder[drive]));
+ BX_INFO((" sector = %u", (unsigned) BX_FD_THIS s.sector[drive]));
+ }
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_result_phase();
+ }
+ else { // more data to transfer
+ Bit32u logical_sector;
+
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads *
+ BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.head[drive] *
+ BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.sector[drive] - 1);
+
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, FROM_FLOPPY);
+ }
+ }
+}
+
+ void
+bx_floppy_ctrl_c::dma_read(Bit8u *data_byte)
+{
+ // A DMA read is from Memory to I/O
+ // We need to write the data_byte which was already transfered from memory
+ // via DMA to I/O (write block to floppy)
+
+ Bit8u drive;
+ Bit32u logical_sector;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+ if (BX_FD_THIS s.pending_command == 0x4d) { // format track in progress
+ --BX_FD_THIS s.format_count;
+ switch (3 - (BX_FD_THIS s.format_count & 0x03)) {
+ case 0:
+ BX_FD_THIS s.cylinder[drive] = *data_byte;
+ break;
+ case 1:
+ if (*data_byte != BX_FD_THIS s.head[drive])
+ BX_ERROR(("head number does not match head field"));
+ break;
+ case 2:
+ BX_FD_THIS s.sector[drive] = *data_byte;
+ break;
+ case 3:
+ if (*data_byte != 2) BX_ERROR(("dma_read: sector size %d not supported", 128<<(*data_byte)));
+ BX_DEBUG(("formatting cylinder %u head %u sector %u",
+ BX_FD_THIS s.cylinder[drive], BX_FD_THIS s.head[drive],
+ BX_FD_THIS s.sector[drive]));
+ for (unsigned i = 0; i < 512; i++) {
+ BX_FD_THIS s.floppy_buffer[i] = BX_FD_THIS s.format_fillbyte;
+ }
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.sector[drive] - 1);
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, TO_FLOPPY);
+ break;
+ }
+ if ((BX_FD_THIS s.format_count == 0) || (DEV_dma_get_tc())) {
+ BX_FD_THIS s.format_count = 0;
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_result_phase();
+ }
+ return;
+ }
+
+ BX_FD_THIS s.floppy_buffer[BX_FD_THIS s.floppy_buffer_index++] = *data_byte;
+
+ if (BX_FD_THIS s.floppy_buffer_index >= 512) {
+ // original assumed all floppies had two sides...now it does not *delete this comment line*
+ logical_sector = (BX_FD_THIS s.cylinder[drive] * BX_FD_THIS s.media[drive].heads * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.head[drive] * BX_FD_THIS s.media[drive].sectors_per_track) +
+ (BX_FD_THIS s.sector[drive] - 1);
+ if ( BX_FD_THIS s.media[drive].write_protected ) {
+ // write protected error
+ BX_INFO(("tried to write disk %u, which is write-protected", drive));
+ // ST0: IC1,0=01 (abnormal termination: started execution but failed)
+ BX_FD_THIS s.status_reg0 = 0x40 | (BX_FD_THIS s.head[drive]<<2) | drive;
+ // ST1: DataError=1, NDAT=1, NotWritable=1, NID=1
+ BX_FD_THIS s.status_reg1 = 0x27; // 0010 0111
+ // ST2: CRCE=1, SERR=1, BCYL=1, NDAM=1.
+ BX_FD_THIS s.status_reg2 = 0x31; // 0011 0001
+ enter_result_phase();
+ return;
+ }
+ floppy_xfer(drive, logical_sector*512, BX_FD_THIS s.floppy_buffer,
+ 512, TO_FLOPPY);
+ increment_sector(); // increment to next sector after writing current one
+ BX_FD_THIS s.floppy_buffer_index = 0;
+ if (DEV_dma_get_tc()) { // Terminal Count line, done
+ BX_FD_THIS s.status_reg0 = (BX_FD_THIS s.head[drive] << 2) | drive;
+ BX_FD_THIS s.status_reg1 = 0;
+ BX_FD_THIS s.status_reg2 = 0;
+
+ if (bx_dbg.floppy) {
+ BX_INFO(("<<WRITE DONE>>"));
+ BX_INFO(("AFTER"));
+ BX_INFO((" drive = %u", (unsigned) drive));
+ BX_INFO((" head = %u", (unsigned) BX_FD_THIS s.head[drive]));
+ BX_INFO((" cylinder = %u", (unsigned) BX_FD_THIS s.cylinder[drive]));
+ BX_INFO((" sector = %u", (unsigned) BX_FD_THIS s.sector[drive]));
+ }
+
+ DEV_dma_set_drq(FLOPPY_DMA_CHAN, 0);
+ enter_result_phase();
+ }
+ else { // more data to transfer
+ } // else
+ } // if BX_FD_THIS s.floppy_buffer_index >= 512
+}
+
+ void
+bx_floppy_ctrl_c::raise_interrupt(void)
+{
+ DEV_pic_raise_irq(6);
+ BX_FD_THIS s.pending_irq = 1;
+ BX_FD_THIS s.reset_sensei = 0;
+}
+
+
+ void
+bx_floppy_ctrl_c::increment_sector(void)
+{
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+
+ // values after completion of data xfer
+ // ??? calculation depends on base_count being multiple of 512
+ BX_FD_THIS s.sector[drive] ++;
+ if (BX_FD_THIS s.sector[drive] > BX_FD_THIS s.media[drive].sectors_per_track) {
+ BX_FD_THIS s.sector[drive] = 1;
+ if (BX_FD_THIS s.multi_track) {
+ BX_FD_THIS s.head[drive] ++;
+ if (BX_FD_THIS s.head[drive] > 1) {
+ BX_FD_THIS s.head[drive] = 0;
+ BX_FD_THIS s.cylinder[drive] ++;
+ }
+ }
+ else {
+ BX_FD_THIS s.cylinder[drive] ++;
+ }
+ if (BX_FD_THIS s.cylinder[drive] >= BX_FD_THIS s.media[drive].tracks) {
+ // Set to 1 past last possible cylinder value.
+ // I notice if I set it to tracks-1, prama linux won't boot.
+ BX_FD_THIS s.cylinder[drive] = BX_FD_THIS s.media[drive].tracks;
+ BX_INFO(("increment_sector: clamping cylinder to max"));
+ }
+ }
+}
+
+ unsigned
+bx_floppy_ctrl_c::set_media_status(unsigned drive, unsigned status)
+{
+ char *path;
+ unsigned type;
+
+ if (drive == 0)
+ type = bx_options.floppya.Otype->get ();
+ else
+ type = bx_options.floppyb.Otype->get ();
+
+ // if setting to the current value, nothing to do
+ if ((status == BX_FD_THIS s.media_present[drive]) &&
+ ((status == 0) || (type == BX_FD_THIS s.media[drive].type)))
+ return(status);
+
+ if (status == 0) {
+ // eject floppy
+ if (BX_FD_THIS s.media[drive].fd >= 0) {
+ close( BX_FD_THIS s.media[drive].fd );
+ BX_FD_THIS s.media[drive].fd = -1;
+ }
+ BX_FD_THIS s.media_present[drive] = 0;
+ if (drive == 0) {
+ bx_options.floppya.Ostatus->set(BX_EJECTED);
+ } else {
+ bx_options.floppyb.Ostatus->set(BX_EJECTED);
+ }
+ BX_FD_THIS s.DIR[drive] |= 0x80; // disk changed line
+ return(0);
+ }
+ else {
+ // insert floppy
+ if (drive == 0) {
+ path = bx_options.floppya.Opath->getptr ();
+ }
+ else {
+ path = bx_options.floppyb.Opath->getptr ();
+ }
+ if (!strcmp(path, "none"))
+ return(0);
+ if (evaluate_media(type, path, & BX_FD_THIS s.media[drive])) {
+ BX_FD_THIS s.media_present[drive] = 1;
+ if (drive == 0) {
+#define MED (BX_FD_THIS s.media[0])
+ BX_INFO(("fd0: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppya.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ bx_options.floppya.Ostatus->set(BX_INSERTED);
+ } else {
+#define MED (BX_FD_THIS s.media[1])
+ BX_INFO(("fd1: '%s' ro=%d, h=%d,t=%d,spt=%d", bx_options.floppyb.Opath->getptr(),
+ MED.write_protected, MED.heads, MED.tracks, MED.sectors_per_track));
+#undef MED
+ bx_options.floppyb.Ostatus->set(BX_INSERTED);
+ }
+ BX_FD_THIS s.DIR[drive] |= 0x80; // disk changed line
+ return(1);
+ }
+ else {
+ BX_FD_THIS s.media_present[drive] = 0;
+ if (drive == 0) {
+ bx_options.floppya.Ostatus->set(BX_EJECTED);
+ } else {
+ bx_options.floppyb.Ostatus->set(BX_EJECTED);
+ }
+ return(0);
+ }
+ }
+}
+
+ unsigned
+bx_floppy_ctrl_c::get_media_status(unsigned drive)
+{
+ return( BX_FD_THIS s.media_present[drive] );
+}
+
+#ifdef O_BINARY
+#define BX_RDONLY O_RDONLY | O_BINARY
+#define BX_RDWR O_RDWR | O_BINARY
+#else
+#define BX_RDONLY O_RDONLY
+#define BX_RDWR O_RDWR
+#endif
+
+ bx_bool
+bx_floppy_ctrl_c::evaluate_media(unsigned type, char *path, floppy_t *media)
+{
+ struct stat stat_buf;
+ int i, ret;
+ int idx = -1;
+#ifdef __linux__
+ struct floppy_struct floppy_geom;
+#endif
+#ifdef WIN32
+ char sTemp[1024];
+ bx_bool raw_floppy = 0;
+ HANDLE hFile;
+ DWORD bytes;
+ DISK_GEOMETRY dg;
+ unsigned tracks, heads, spt;
+#endif
+
+ if (type == BX_FLOPPY_NONE)
+ return(0);
+
+ //If media file is already open, close it before reopening.
+ if(media->fd >=0) {
+ close(media->fd);
+ media->fd=-1;
+ }
+
+ // open media file (image file or device)
+ media->write_protected = 0;
+#ifdef macintosh
+ media->fd = 0;
+ if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+#endif
+#ifdef WIN32
+ if ( (isalpha(path[0])) && (path[1] == ':') && (strlen(path) == 2) ) {
+ raw_floppy = 1;
+ wsprintf(sTemp, "\\\\.\\%s", path);
+ hFile = CreateFile(sTemp, GENERIC_READ, FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ BX_ERROR(("Cannot open floppy drive"));
+ return(0);
+ } else {
+ if (!DeviceIoControl(hFile, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(dg), &bytes, NULL)) {
+ BX_ERROR(("No media in floppy drive"));
+ CloseHandle(hFile);
+ return(0);
+ } else {
+ tracks = (unsigned)dg.Cylinders.QuadPart;
+ heads = (unsigned)dg.TracksPerCylinder;
+ spt = (unsigned)dg.SectorsPerTrack;
+ }
+ CloseHandle(hFile);
+ }
+ media->fd = open(sTemp, BX_RDWR);
+ } else {
+ media->fd = open(path, BX_RDWR);
+ }
+#else
+ media->fd = open(path, BX_RDWR);
+#endif
+
+ if (media->fd < 0) {
+ BX_INFO(( "tried to open '%s' read/write: %s",path,strerror(errno) ));
+ // try opening the file read-only
+ media->write_protected = 1;
+#ifdef macintosh
+ media->fd = 0;
+ if (strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+#endif
+#ifdef WIN32
+ if (raw_floppy == 1) {
+ media->fd = open(sTemp, BX_RDONLY);
+ } else {
+ media->fd = open(path, BX_RDONLY);
+ }
+#else
+ media->fd = open(path, BX_RDONLY);
+#endif
+ if (media->fd < 0) {
+ // failed to open read-only too
+ BX_INFO(( "tried to open '%s' read only: %s",path,strerror(errno) ));
+ media->type = type;
+ return(0);
+ }
+ }
+
+#if BX_WITH_MACOS
+ if (!strcmp(bx_options.floppya.Opath->getptr (), SuperDrive))
+ ret = fd_stat(&stat_buf);
+ else
+ ret = fstat(media->fd, &stat_buf);
+#elif defined(WIN32)
+ if (raw_floppy) {
+ memset (&stat_buf, 0, sizeof(stat_buf));
+ stat_buf.st_mode = S_IFCHR;
+ ret = 0;
+ } else {
+ ret = fstat(media->fd, &stat_buf);
+ }
+#else
+ // unix
+ ret = fstat(media->fd, &stat_buf);
+#endif
+ if (ret) {
+ BX_PANIC(("fstat floppy 0 drive image file returns error: %s", strerror(errno)));
+ return(0);
+ }
+
+ for (i = 0; i < 8; i++) {
+ if (type == floppy_type[i].id) idx = i;
+ }
+ if (idx == -1 ) {
+ BX_PANIC(("evaluate_media: unknown media type"));
+ return(0);
+ }
+ if ( S_ISREG(stat_buf.st_mode) ) {
+ // regular file
+ switch (type) {
+ // use CMOS reserved types
+ case BX_FLOPPY_160K: // 160K 5.25"
+ case BX_FLOPPY_180K: // 180K 5.25"
+ case BX_FLOPPY_320K: // 320K 5.25"
+ // standard floppy types
+ case BX_FLOPPY_360K: // 360K 5.25"
+ case BX_FLOPPY_720K: // 720K 3.5"
+ case BX_FLOPPY_1_2: // 1.2M 5.25"
+ case BX_FLOPPY_2_88: // 2.88M 3.5"
+ media->type = type;
+ media->tracks = floppy_type[idx].trk;
+ media->heads = floppy_type[idx].hd;
+ media->sectors_per_track = floppy_type[idx].spt;
+ media->sectors = floppy_type[idx].sectors;
+ if (stat_buf.st_size > (media->sectors * 512)) {
+ BX_INFO(("evaluate_media: size of file '%s' (%lu) too large for selected type",
+ path, (unsigned long) stat_buf.st_size));
+ return(0);
+ }
+ break;
+ default: // 1.44M 3.5"
+ media->type = type;
+ if (stat_buf.st_size <= 1474560) {
+ media->tracks = floppy_type[idx].trk;
+ media->heads = floppy_type[idx].hd;
+ media->sectors_per_track = floppy_type[idx].spt;
+ }
+ else if (stat_buf.st_size == 1720320) {
+ media->sectors_per_track = 21;
+ media->tracks = 80;
+ media->heads = 2;
+ }
+ else if (stat_buf.st_size == 1763328) {
+ media->sectors_per_track = 21;
+ media->tracks = 82;
+ media->heads = 2;
+ }
+ else {
+ BX_INFO(("evaluate_media: file '%s' of unknown size %lu",
+ path, (unsigned long) stat_buf.st_size));
+ return(0);
+ }
+ media->sectors = media->heads * media->tracks * media->sectors_per_track;
+ }
+ return(1); // success
+ }
+
+ else if ( S_ISCHR(stat_buf.st_mode)
+#if BX_WITH_MACOS == 0
+#ifdef S_ISBLK
+ || S_ISBLK(stat_buf.st_mode)
+#endif
+#endif
+ ) {
+ // character or block device
+ // assume media is formatted to typical geometry for drive
+ media->type = type;
+#ifdef __linux__
+ if (ioctl(media->fd, FDGETPRM, &floppy_geom) < 0) {
+ BX_ERROR(("cannot determine media geometry"));
+ return(0);
+ }
+ media->tracks = floppy_geom.track;
+ media->heads = floppy_geom.head;
+ media->sectors_per_track = floppy_geom.sect;
+ media->sectors = floppy_geom.size;
+#elif defined(WIN32)
+ media->tracks = tracks;
+ media->heads = heads;
+ media->sectors_per_track = spt;
+ media->sectors = media->heads * media->tracks * media->sectors_per_track;
+#else
+ media->tracks = floppy_type[idx].trk;
+ media->heads = floppy_type[idx].hd;
+ media->sectors_per_track = floppy_type[idx].spt;
+ media->sectors = floppy_type[idx].sectors;
+#endif
+ return(1); // success
+ }
+ else {
+ // unknown file type
+ BX_INFO(("unknown mode type"));
+ return(0);
+ }
+}
+
+
+void
+bx_floppy_ctrl_c::enter_result_phase(void)
+{
+
+ Bit8u drive;
+
+ drive = BX_FD_THIS s.DOR & 0x03;
+
+ /* these are always the same */
+ BX_FD_THIS s.result_index = 0;
+ BX_FD_THIS s.main_status_reg = FD_MS_MRQ | FD_MS_DIO | FD_MS_BUSY;
+
+ /* invalid command */
+ if ((BX_FD_THIS s.status_reg0 & 0xc0) == 0x80) {
+ BX_FD_THIS s.result_size = 1;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
+ return;
+ }
+
+ switch (BX_FD_THIS s.pending_command) {
+ case 0x04: // get status
+ BX_FD_THIS s.result_size = 1;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg3;
+ break;
+ case 0x08: // sense interrupt
+ BX_FD_THIS s.result_size = 2;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
+ BX_FD_THIS s.result[1] = BX_FD_THIS s.cylinder[drive];
+ break;
+ case 0x4a: // read ID
+ case 0x4d: // format track
+ case 0x46: // read normal data
+ case 0x66:
+ case 0xc6:
+ case 0xe6:
+ case 0x45: // write normal data
+ case 0xc5:
+ BX_FD_THIS s.result_size = 7;
+ BX_FD_THIS s.result[0] = BX_FD_THIS s.status_reg0;
+ BX_FD_THIS s.result[1] = BX_FD_THIS s.status_reg1;
+ BX_FD_THIS s.result[2] = BX_FD_THIS s.status_reg2;
+ BX_FD_THIS s.result[3] = BX_FD_THIS s.cylinder[drive];
+ BX_FD_THIS s.result[4] = BX_FD_THIS s.head[drive];
+ BX_FD_THIS s.result[5] = BX_FD_THIS s.sector[drive];
+ BX_FD_THIS s.result[6] = 2; /* sector size code */
+ raise_interrupt();
+ break;
+ }
+}
+
+void
+bx_floppy_ctrl_c::enter_idle_phase(void)
+{
+ BX_FD_THIS s.main_status_reg &= 0x0f; // leave drive status untouched
+ BX_FD_THIS s.main_status_reg |= FD_MS_MRQ; // data register ready
+
+ BX_FD_THIS s.command_complete = 1; /* waiting for new command */
+ BX_FD_THIS s.command_index = 0;
+ BX_FD_THIS s.command_size = 0;
+ BX_FD_THIS s.pending_command = 0;
+
+ BX_FD_THIS s.floppy_buffer_index = 0;
+}
+
diff --git a/tools/ioemu/iodev/floppy.h b/tools/ioemu/iodev/floppy.h
new file mode 100644
index 0000000000..d874539900
--- /dev/null
+++ b/tools/ioemu/iodev/floppy.h
@@ -0,0 +1,138 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: floppy.h,v 1.16 2002/11/30 09:39:29 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+#define FROM_FLOPPY 10
+#define TO_FLOPPY 11
+
+#if BX_USE_FD_SMF
+# define BX_FD_SMF static
+# define BX_FD_THIS theFloppyController->
+#else
+# define BX_FD_SMF
+# define BX_FD_THIS this->
+#endif
+
+typedef struct {
+ int fd; /* file descriptor of floppy image file */
+ unsigned sectors_per_track; /* number of sectors/track */
+ unsigned sectors; /* number of formatted sectors on diskette */
+ unsigned tracks; /* number of tracks */
+ unsigned heads; /* number of heads */
+ unsigned type;
+ unsigned write_protected;
+ } floppy_t;
+
+class bx_floppy_ctrl_c : public bx_floppy_stub_c {
+public:
+
+ bx_floppy_ctrl_c(void);
+ ~bx_floppy_ctrl_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+ virtual unsigned set_media_status(unsigned drive, unsigned status);
+ virtual unsigned get_media_status(unsigned drive);
+
+private:
+
+ struct {
+ Bit8u data_rate;
+
+ Bit8u command[10]; /* largest command size ??? */
+ Bit8u command_index;
+ Bit8u command_size;
+ bx_bool command_complete;
+ Bit8u pending_command;
+
+ bx_bool multi_track;
+ bx_bool pending_irq;
+ Bit8u reset_sensei;
+ Bit8u format_count;
+ Bit8u format_fillbyte;
+
+ Bit8u result[10];
+ Bit8u result_index;
+ Bit8u result_size;
+
+ Bit8u DOR; // Digital Ouput Register
+ Bit8u TDR; // Tape Drive Register
+ Bit8u cylinder[4]; // really only using 2 drives
+ Bit8u head[4]; // really only using 2 drives
+ Bit8u sector[4]; // really only using 2 drives
+
+ /* MAIN STATUS REGISTER
+ * b7: MRQ: main request 1=data register ready 0=data register not ready
+ * b6: DIO: data input/output:
+ * 1=controller->CPU (ready for data read)
+ * 0=CPU->controller (ready for data write)
+ * b5: NDMA: non-DMA mode: 1=controller not in DMA modes
+ * 0=controller in DMA mode
+ * b4: BUSY: instruction(device busy) 1=active 0=not active
+ * b3-0: ACTD, ACTC, ACTB, ACTA:
+ * drive D,C,B,A in positioning mode 1=active 0=not active
+ */
+ Bit8u main_status_reg;
+
+ Bit8u status_reg0;
+ Bit8u status_reg1;
+ Bit8u status_reg2;
+ Bit8u status_reg3;
+
+ // drive field allows up to 4 drives, even though probably only 2 will
+ // ever be used.
+ floppy_t media[4];
+ unsigned num_supported_floppies;
+ Bit8u floppy_buffer[512+2]; // 2 extra for good measure
+ unsigned floppy_buffer_index;
+ int floppy_timer_index;
+ bx_bool media_present[2];
+ Bit8u device_type[4];
+ Bit8u DIR[4]; // Digital Input Register:
+ // b7: 0=diskette is present and has not been changed
+ // 1=diskette missing or changed
+ } s; // state information
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_FD_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+ BX_FD_SMF void dma_write(Bit8u *data_byte);
+ BX_FD_SMF void dma_read(Bit8u *data_byte);
+ BX_FD_SMF void floppy_command(void);
+ BX_FD_SMF void floppy_xfer(Bit8u drive, Bit32u offset, Bit8u *buffer, Bit32u bytes, Bit8u direction);
+ BX_FD_SMF void raise_interrupt(void);
+ BX_FD_SMF void enter_idle_phase(void);
+ BX_FD_SMF void enter_result_phase(void);
+ static void timer_handler(void *);
+
+public:
+ BX_FD_SMF void timer(void);
+ BX_FD_SMF void increment_sector(void);
+ BX_FD_SMF bx_bool evaluate_media(unsigned type, char *path, floppy_t *floppy);
+ };
diff --git a/tools/ioemu/iodev/gameport.cc b/tools/ioemu/iodev/gameport.cc
new file mode 100644
index 0000000000..9adefa6551
--- /dev/null
+++ b/tools/ioemu/iodev/gameport.cc
@@ -0,0 +1,242 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: gameport.cc,v 1.5 2003/12/29 21:48:56 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2003 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// Standard PC gameport
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#ifdef __linux__
+
+#include <linux/joystick.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#elif defined(WIN32)
+
+#ifndef JOY_BUTTON1
+#define JOY_BUTTON1 1
+#define JOY_BUTTON2 2
+UINT STDCALL joyGetPos(UINT, LPJOYINFO);
+#endif
+
+#define JOYSTICKID1 0
+
+#endif
+
+#define LOG_THIS theGameport->
+
+bx_gameport_c *theGameport = NULL;
+
+ int
+libgameport_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theGameport = new bx_gameport_c ();
+ bx_devices.pluginGameport = theGameport;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theGameport, BX_PLUGIN_GAMEPORT);
+ return(0); // Success
+}
+
+ void
+libgameport_LTX_plugin_fini(void)
+{
+}
+
+bx_gameport_c::bx_gameport_c(void)
+{
+ put("GAME");
+ settype(EXTFPUIRQLOG);
+}
+
+bx_gameport_c::~bx_gameport_c(void)
+{
+ if (joyfd >= 0) close(joyfd);
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_gameport_c::init(void)
+{
+ // Allocate the gameport IO address range 0x200..0x207
+ for (unsigned addr=0x200; addr<0x208; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "Gameport", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "Gameport", 1);
+ }
+
+ BX_GAMEPORT_THIS port = 0xf0;
+ BX_GAMEPORT_THIS write_usec = 0;
+ BX_GAMEPORT_THIS timer_x = 0;
+ BX_GAMEPORT_THIS timer_y = 0;
+
+#ifdef __linux__
+ BX_GAMEPORT_THIS joyfd = open("/dev/input/js0", O_RDONLY);
+ if (BX_GAMEPORT_THIS joyfd >= 0) {
+ for (unsigned i=0; i<4; i++) poll_joydev();
+ }
+#elif defined(WIN32)
+ JOYINFO joypos;
+ if (joyGetPos(JOYSTICKID1, &joypos) == JOYERR_NOERROR) {
+ BX_GAMEPORT_THIS joyfd = 1;
+ } else {
+ BX_GAMEPORT_THIS joyfd = -1;
+ }
+#else
+ BX_GAMEPORT_THIS joyfd = -1;
+#endif
+}
+
+ void
+bx_gameport_c::reset(unsigned type)
+{
+ // nothing for now
+}
+
+ void
+bx_gameport_c::poll_joydev(void)
+{
+#ifdef __linux__
+ struct js_event e;
+ fd_set joyfds;
+ struct timeval tv;
+
+ memset(&tv, 0, sizeof(tv));
+ FD_ZERO(&joyfds);
+ FD_SET(BX_GAMEPORT_THIS joyfd, &joyfds);
+ e.type = 0;
+ if (select(BX_GAMEPORT_THIS joyfd+1, &joyfds, NULL, NULL, &tv)) {
+ read(BX_GAMEPORT_THIS joyfd, &e, sizeof(struct js_event));
+ if (e.type & JS_EVENT_BUTTON) {
+ if (e.value == 1) {
+ BX_GAMEPORT_THIS port &= ~(0x10 << e.number);
+ } else {
+ BX_GAMEPORT_THIS port |= (0x10 << e.number);
+ }
+ }
+ if (e.type & JS_EVENT_AXIS) {
+ if (e.number == 0) {
+ BX_GAMEPORT_THIS delay_x = 25 + ((e.value + 0x8000) / 60);
+ }
+ if (e.number == 1) {
+ BX_GAMEPORT_THIS delay_y = 25 + ((e.value + 0x8000) / 62);
+ }
+ }
+ }
+#elif defined(WIN32)
+ JOYINFO joypos;
+ if (joyGetPos(JOYSTICKID1, &joypos) == JOYERR_NOERROR) {
+ if (joypos.wButtons & JOY_BUTTON1) {
+ BX_GAMEPORT_THIS port &= ~0x10;
+ } else {
+ BX_GAMEPORT_THIS port |= 0x10;
+ }
+ if (joypos.wButtons & JOY_BUTTON2) {
+ BX_GAMEPORT_THIS port &= ~0x20;
+ } else {
+ BX_GAMEPORT_THIS port |= 0x20;
+ }
+ BX_GAMEPORT_THIS delay_x = 25 + (joypos.wXpos / 60);
+ BX_GAMEPORT_THIS delay_y = 25 + (joypos.wYpos / 60);
+ }
+#endif
+}
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_gameport_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_GAME_SMF
+ bx_gameport_c *class_ptr = (bx_gameport_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_gameport_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_GAME_SMF
+ Bit64u usec;
+
+ if (BX_GAMEPORT_THIS joyfd >= 0) {
+ poll_joydev();
+ usec = bx_pc_system.time_usec();
+ if (BX_GAMEPORT_THIS timer_x) {
+ if ((usec - BX_GAMEPORT_THIS write_usec) >= BX_GAMEPORT_THIS delay_x) {
+ BX_GAMEPORT_THIS port &= 0xfe;
+ BX_GAMEPORT_THIS timer_x = 0;
+ }
+ }
+ if (BX_GAMEPORT_THIS timer_y) {
+ if ((usec - BX_GAMEPORT_THIS write_usec) >= BX_GAMEPORT_THIS delay_y) {
+ BX_GAMEPORT_THIS port &= 0xfd;
+ BX_GAMEPORT_THIS timer_y = 0;
+ }
+ }
+ } else {
+ BX_DEBUG(("read: joystick not present"));
+ }
+ return BX_GAMEPORT_THIS port;
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_gameport_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_GAME_SMF
+ bx_gameport_c *class_ptr = (bx_gameport_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_gameport_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_GAME_SMF
+
+ BX_GAMEPORT_THIS write_usec = bx_pc_system.time_usec();
+ BX_GAMEPORT_THIS timer_x = 1;
+ BX_GAMEPORT_THIS timer_y = 1;
+ BX_GAMEPORT_THIS port |= 0x0f;
+}
diff --git a/tools/ioemu/iodev/gameport.h b/tools/ioemu/iodev/gameport.h
new file mode 100644
index 0000000000..d4acdddf69
--- /dev/null
+++ b/tools/ioemu/iodev/gameport.h
@@ -0,0 +1,63 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: gameport.h,v 1.1 2003/06/21 12:55:19 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2003 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#if BX_USE_GAME_SMF
+# define BX_GAMEPORT_SMF static
+# define BX_GAMEPORT_THIS theGameport->
+#else
+# define BX_GAMEPORT_SMF
+# define BX_GAMEPORT_THIS this->
+#endif
+
+
+class bx_gameport_c : public bx_devmodel_c {
+
+public:
+ bx_gameport_c(void);
+ ~bx_gameport_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+
+private:
+
+ int joyfd;
+ Bit8u port;
+ Bit16u delay_x;
+ Bit16u delay_y;
+ bx_bool timer_x;
+ bx_bool timer_y;
+ Bit64u write_usec;
+
+ BX_GAMEPORT_SMF void poll_joydev(void);
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_GAME_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+ };
diff --git a/tools/ioemu/iodev/guest2host.h b/tools/ioemu/iodev/guest2host.h
new file mode 100644
index 0000000000..0003662aa2
--- /dev/null
+++ b/tools/ioemu/iodev/guest2host.h
@@ -0,0 +1,77 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: guest2host.h,v 1.8 2002/12/06 18:48:08 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+#define BX_MAX_G2H_CHANNELS 8
+#define BX_G2H_ERROR ((unsigned) -1)
+ // IO port number for this interface. Align on dword boundary.
+#define BX_G2H_PORT 0x2000
+ // Magic number which is first dword passed by guest
+#define BX_G2H_MAGIC 0xffeeddcc
+ // Number of dwords in packet from guest
+#define BX_G2H_PACKET_SIZE 5
+
+
+
+typedef Bit32u bx_guest_packet_t[BX_G2H_PACKET_SIZE];
+typedef void (*bx_g2h_callback_t)(bx_guest_packet_t *);
+
+
+
+class bx_g2h_c : public logfunctions {
+public:
+ bx_g2h_c(void);
+ ~bx_g2h_c(void);
+ static void init(void);
+ void reset (unsigned type);
+ unsigned acquire_channel(bx_g2h_callback_t);
+ unsigned deacquire_channel(unsigned channel);
+
+private:
+
+ static Bit32u inp_handler(void *this_ptr, Bit32u addr, unsigned io_len);
+ static void outp_handler(void *this_ptr, Bit32u addr,
+ Bit32u value, unsigned io_len);
+ // state info
+ struct {
+ struct {
+ bx_g2h_callback_t f;
+ bx_bool used;
+ } callback[BX_MAX_G2H_CHANNELS];
+
+ // Define the data received from the guest OS.
+ // dword0: magic number, should be BX_G2H_MAGIC
+ // dword1: channel ID
+ // dword2: address of data structure in guest physical memory
+ // dword3: size of data structure in guest physical memory
+ // dword4: address of return data structure in guest physical memory
+ unsigned packet_count;
+ bx_guest_packet_t guest_packet;
+ } s;
+ };
+
+extern bx_g2h_c bx_g2h;
diff --git a/tools/ioemu/iodev/harddrv.cc b/tools/ioemu/iodev/harddrv.cc
new file mode 100644
index 0000000000..24a9d41efa
--- /dev/null
+++ b/tools/ioemu/iodev/harddrv.cc
@@ -0,0 +1,4880 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: harddrv.cc,v 1.114.2.2 2004/02/06 22:14:35 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Useful docs:
+// AT Attachment with Packet Interface
+// working draft by T13 at www.t13.org
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#if BX_HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#define LOG_THIS theHardDrive->
+
+// WARNING: dangerous options!
+// These options provoke certain kinds of errors for testing purposes when they
+// are set to a nonzero value. DO NOT ENABLE THEM when using any disk image
+// you care about.
+#define TEST_READ_BEYOND_END 0
+#define TEST_WRITE_BEYOND_END 0
+#ifdef __GNUC__
+# if TEST_READ_BEYOND_END || TEST_WRITE_BEYOND_END
+# warning BEWARE: Dangerous options are enabled in harddrv.cc. If you are not trying to provoke hard drive errors you should disable them right now.
+# endif
+#endif
+// end of dangerous options.
+
+
+#define INDEX_PULSE_CYCLE 10
+
+#define PACKET_SIZE 12
+
+static unsigned max_multiple_sectors = 0; // was 0x3f
+static unsigned curr_multiple_sectors = 0; // was 0x3f
+
+// some packet handling macros
+#define EXTRACT_FIELD(arr,byte,start,num_bits) (((arr)[(byte)] >> (start)) & ((1 << (num_bits)) - 1))
+#define get_packet_field(c,b,s,n) (EXTRACT_FIELD((BX_SELECTED_CONTROLLER((c)).buffer),(b),(s),(n)))
+#define get_packet_byte(c,b) (BX_SELECTED_CONTROLLER((c)).buffer[(b)])
+#define get_packet_word(c,b) (((uint16)BX_SELECTED_CONTROLLER((c)).buffer[(b)] << 8) | BX_SELECTED_CONTROLLER((c)).buffer[(b)+1])
+
+
+#define BX_CONTROLLER(c,a) (BX_HD_THIS channels[(c)].drives[(a)]).controller
+#define BX_DRIVE(c,a) (BX_HD_THIS channels[(c)].drives[(a)])
+
+#define BX_DRIVE_IS_PRESENT(c,a) (BX_HD_THIS channels[(c)].drives[(a)].device_type != IDE_NONE)
+#define BX_DRIVE_IS_HD(c,a) (BX_HD_THIS channels[(c)].drives[(a)].device_type == IDE_DISK)
+#define BX_DRIVE_IS_CD(c,a) (BX_HD_THIS channels[(c)].drives[(a)].device_type == IDE_CDROM)
+
+#define BX_MASTER_IS_PRESENT(c) BX_DRIVE_IS_PRESENT((c),0)
+#define BX_SLAVE_IS_PRESENT(c) BX_DRIVE_IS_PRESENT((c),1)
+#define BX_ANY_IS_PRESENT(c) (BX_DRIVE_IS_PRESENT((c),0) || BX_DRIVE_IS_PRESENT((c),1))
+
+#define BX_SELECTED_CONTROLLER(c) (BX_CONTROLLER((c),BX_HD_THIS channels[(c)].drive_select))
+#define BX_SELECTED_DRIVE(c) (BX_DRIVE((c),BX_HD_THIS channels[(c)].drive_select))
+#define BX_MASTER_SELECTED(c) (!BX_HD_THIS channels[(c)].drive_select)
+#define BX_SLAVE_SELECTED(c) (BX_HD_THIS channels[(c)].drive_select)
+
+#define BX_SELECTED_IS_PRESENT(c) (BX_DRIVE_IS_PRESENT((c),BX_SLAVE_SELECTED((c))))
+#define BX_SELECTED_IS_HD(c) (BX_DRIVE_IS_HD((c),BX_SLAVE_SELECTED((c))))
+#define BX_SELECTED_IS_CD(c) (BX_DRIVE_IS_CD((c),BX_SLAVE_SELECTED((c))))
+
+#define BX_SELECTED_MODEL(c) (BX_HD_THIS channels[(c)].drives[BX_HD_THIS channels[(c)].drive_select].model_no)
+#define BX_SELECTED_TYPE_STRING(channel) ((BX_SELECTED_IS_CD(channel)) ? "CD-ROM" : "DISK")
+
+#define WRITE_FEATURES(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).features = _a; BX_CONTROLLER((c),1).features = _a; } while(0)
+#define WRITE_SECTOR_COUNT(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).sector_count = _a; BX_CONTROLLER((c),1).sector_count = _a; } while(0)
+#define WRITE_SECTOR_NUMBER(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).sector_no = _a; BX_CONTROLLER((c),1).sector_no = _a; } while(0)
+#define WRITE_CYLINDER_LOW(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).cylinder_no = (BX_CONTROLLER((c),0).cylinder_no & 0xff00) | _a; BX_CONTROLLER((c),1).cylinder_no = (BX_CONTROLLER((c),1).cylinder_no & 0xff00) | _a; } while(0)
+#define WRITE_CYLINDER_HIGH(c,a) do { uint16 _a = a; BX_CONTROLLER((c),0).cylinder_no = (_a << 8) | (BX_CONTROLLER((c),0).cylinder_no & 0xff); BX_CONTROLLER((c),1).cylinder_no = (_a << 8) | (BX_CONTROLLER((c),1).cylinder_no & 0xff); } while(0)
+#define WRITE_HEAD_NO(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).head_no = _a; BX_CONTROLLER((c),1).head_no = _a; } while(0)
+#define WRITE_LBA_MODE(c,a) do { uint8 _a = a; BX_CONTROLLER((c),0).lba_mode = _a; BX_CONTROLLER((c),1).lba_mode = _a; } while(0)
+
+bx_hard_drive_c *theHardDrive = NULL;
+
+ int
+libharddrv_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theHardDrive = new bx_hard_drive_c ();
+ bx_devices.pluginHardDrive = theHardDrive;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theHardDrive, BX_PLUGIN_HARDDRV);
+ return(0); // Success
+}
+
+ void
+libharddrv_LTX_plugin_fini(void)
+{
+}
+
+bx_hard_drive_c::bx_hard_drive_c(void)
+{
+#if DLL_HD_SUPPORT
+# error code must be fixed to use DLL_HD_SUPPORT and 4 ata channels
+#endif
+
+ for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ channels[channel].drives[0].hard_drive = NULL;
+ channels[channel].drives[1].hard_drive = NULL;
+ put("HD");
+ settype(HDLOG);
+ }
+}
+
+
+bx_hard_drive_c::~bx_hard_drive_c(void)
+{
+ BX_DEBUG(("Exit."));
+ for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if (channels[channel].drives[0].hard_drive != NULL ) /* DT 17.12.2001 21:55 */
+ {
+ delete channels[channel].drives[0].hard_drive;
+ channels[channel].drives[0].hard_drive = NULL;
+ }
+ if ( channels[channel].drives[1].hard_drive != NULL )
+ {
+ delete channels[channel].drives[1].hard_drive;
+ channels[channel].drives[1].hard_drive = NULL; /* DT 17.12.2001 21:56 */
+ }
+ }
+}
+
+
+
+
+ void
+bx_hard_drive_c::init(void)
+{
+ Bit8u channel;
+ char string[5];
+
+ BX_DEBUG(("Init $Id: harddrv.cc,v 1.114.2.2 2004/02/06 22:14:35 danielg4 Exp $"));
+
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if (bx_options.ata[channel].Opresent->get() == 1) {
+ BX_HD_THIS channels[channel].ioaddr1 = bx_options.ata[channel].Oioaddr1->get();
+ BX_HD_THIS channels[channel].ioaddr2 = bx_options.ata[channel].Oioaddr2->get();
+ BX_HD_THIS channels[channel].irq = bx_options.ata[channel].Oirq->get();
+
+ // Coherency check
+ if ( (BX_HD_THIS channels[channel].ioaddr1 == 0) ||
+ (BX_HD_THIS channels[channel].ioaddr2 == 0) ||
+ (BX_HD_THIS channels[channel].irq == 0) ) {
+ BX_PANIC(("incoherency for ata channel %d: io1=0x%x, io2=%x, irq=%d",
+ channel,
+ BX_HD_THIS channels[channel].ioaddr1,
+ BX_HD_THIS channels[channel].ioaddr2,
+ BX_HD_THIS channels[channel].irq));
+ }
+ }
+ else {
+ BX_HD_THIS channels[channel].ioaddr1 = 0;
+ BX_HD_THIS channels[channel].ioaddr2 = 0;
+ BX_HD_THIS channels[channel].irq = 0;
+ }
+ }
+
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ sprintf(string ,"ATA%d", channel);
+
+ if (BX_HD_THIS channels[channel].irq != 0)
+ DEV_register_irq(BX_HD_THIS channels[channel].irq, strdup(string));
+
+ if (BX_HD_THIS channels[channel].ioaddr1 != 0) {
+ DEV_register_ioread_handler(this, read_handler,
+ BX_HD_THIS channels[channel].ioaddr1, strdup(string), 6);
+ DEV_register_iowrite_handler(this, write_handler,
+ BX_HD_THIS channels[channel].ioaddr1, strdup(string), 6);
+ for (unsigned addr=0x1; addr<=0x7; addr++) {
+ DEV_register_ioread_handler(this, read_handler,
+ BX_HD_THIS channels[channel].ioaddr1+addr, strdup(string), 1);
+ DEV_register_iowrite_handler(this, write_handler,
+ BX_HD_THIS channels[channel].ioaddr1+addr, strdup(string), 1);
+ }
+ }
+
+ // We don't want to register addresses 0x3f6 and 0x3f7 as they are handled by the floppy controller
+ if ((BX_HD_THIS channels[channel].ioaddr2 != 0) && (BX_HD_THIS channels[channel].ioaddr2 != 0x3f0)) {
+ for (unsigned addr=0x6; addr<=0x7; addr++) {
+ DEV_register_ioread_handler(this, read_handler,
+ BX_HD_THIS channels[channel].ioaddr2+addr, strdup(string), 1);
+ DEV_register_iowrite_handler(this, write_handler,
+ BX_HD_THIS channels[channel].ioaddr2+addr, strdup(string), 1);
+ }
+ }
+
+ BX_HD_THIS channels[channel].drive_select = 0;
+ }
+
+ channel = 0;
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ for (Bit8u device=0; device<2; device ++) {
+
+ // Initialize controller state, even if device is not present
+ BX_CONTROLLER(channel,device).status.busy = 0;
+ BX_CONTROLLER(channel,device).status.drive_ready = 1;
+ BX_CONTROLLER(channel,device).status.write_fault = 0;
+ BX_CONTROLLER(channel,device).status.seek_complete = 1;
+ BX_CONTROLLER(channel,device).status.drq = 0;
+ BX_CONTROLLER(channel,device).status.corrected_data = 0;
+ BX_CONTROLLER(channel,device).status.index_pulse = 0;
+ BX_CONTROLLER(channel,device).status.index_pulse_count = 0;
+ BX_CONTROLLER(channel,device).status.err = 0;
+
+ BX_CONTROLLER(channel,device).error_register = 0x01; // diagnostic code: no error
+ BX_CONTROLLER(channel,device).head_no = 0;
+ BX_CONTROLLER(channel,device).sector_count = 1;
+ BX_CONTROLLER(channel,device).sector_no = 1;
+ BX_CONTROLLER(channel,device).cylinder_no = 0;
+ BX_CONTROLLER(channel,device).current_command = 0x00;
+ BX_CONTROLLER(channel,device).buffer_index = 0;
+
+ BX_CONTROLLER(channel,device).control.reset = 0;
+ BX_CONTROLLER(channel,device).control.disable_irq = 0;
+ BX_CONTROLLER(channel,device).reset_in_progress = 0;
+
+ BX_CONTROLLER(channel,device).sectors_per_block = 0x80;
+ BX_CONTROLLER(channel,device).lba_mode = 0;
+
+ BX_CONTROLLER(channel,device).features = 0;
+
+ // If not present
+ BX_HD_THIS channels[channel].drives[device].device_type = IDE_NONE;
+ if (!bx_options.atadevice[channel][device].Opresent->get()) {
+ continue;
+ }
+
+ // Make model string
+ strncpy((char*)BX_HD_THIS channels[channel].drives[device].model_no,
+ bx_options.atadevice[channel][device].Omodel->getptr(), 40);
+ while (strlen((char *)BX_HD_THIS channels[channel].drives[device].model_no) < 40) {
+ strcat ((char*)BX_HD_THIS channels[channel].drives[device].model_no, " ");
+ }
+
+ if (bx_options.atadevice[channel][device].Otype->get() == BX_ATA_DEVICE_DISK) {
+ BX_DEBUG(( "Hard-Disk on target %d/%d",channel,device));
+ BX_HD_THIS channels[channel].drives[device].device_type = IDE_DISK;
+
+ int cyl = bx_options.atadevice[channel][device].Ocylinders->get ();
+ int heads = bx_options.atadevice[channel][device].Oheads->get ();
+ int spt = bx_options.atadevice[channel][device].Ospt->get ();
+ Bit64u disk_size = (Bit64u)cyl * heads * spt * 512;
+
+ /* instantiate the right class */
+ switch (bx_options.atadevice[channel][device].Omode->get()) {
+
+ case BX_ATA_MODE_FLAT:
+ BX_INFO(("HD on ata%d-%d: '%s' 'flat' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new default_image_t();
+ break;
+
+ case BX_ATA_MODE_CONCAT:
+ BX_INFO(("HD on ata%d-%d: '%s' 'concat' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new concat_image_t();
+ break;
+
+#if EXTERNAL_DISK_SIMULATOR
+ case BX_ATA_MODE_EXTDISKSIM:
+ BX_INFO(("HD on ata%d-%d: '%s' 'External Simulator' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new EXTERNAL_DISK_SIMULATOR_CLASS();
+ break;
+#endif //EXTERNAL_DISK_SIMULATOR
+
+#if DLL_HD_SUPPORT
+ case BX_ATA_MODE_DLL_HD:
+ BX_INFO(("HD on ata%d-%d: '%s' 'dll' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new dll_image_t();
+ break;
+#endif //DLL_HD_SUPPORT
+
+ case BX_ATA_MODE_SPARSE:
+ BX_INFO(("HD on ata%d-%d: '%s' 'sparse' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new sparse_image_t();
+ break;
+
+#if 0
+ case BX_ATA_MODE_VMWARE3:
+ BX_INFO(("HD on ata%d-%d: '%s' 'vmware3' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new vmware3_image_t();
+ break;
+
+ case BX_ATA_MODE_SPLIT:
+ BX_INFO(("HD on ata%d-%d: '%s' 'split' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new split_image_t();
+ break;
+#endif
+
+ case BX_ATA_MODE_UNDOABLE:
+ BX_INFO(("HD on ata%d-%d: '%s' 'undoable' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new undoable_image_t(disk_size,
+ bx_options.atadevice[channel][device].Ojournal->getptr());
+ break;
+
+ case BX_ATA_MODE_GROWING:
+ BX_INFO(("HD on ata%d-%d: '%s' 'growing' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new growing_image_t(disk_size);
+ break;
+
+ case BX_ATA_MODE_VOLATILE:
+ BX_INFO(("HD on ata%d-%d: '%s' 'volatile' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new volatile_image_t(disk_size,
+ bx_options.atadevice[channel][device].Ojournal->getptr());
+ break;
+
+#if 0
+#if BX_COMPRESSED_HD_SUPPORT
+ case BX_ATA_MODE_Z_UNDOABLE:
+ BX_PANIC(("z-undoable disk support not implemented"));
+ BX_INFO(("HD on ata%d-%d: '%s' 'z-undoable' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new z_undoable_image_t(disk_size,
+ bx_options.atadevice[channel][device].Ojournal->getptr());
+ break;
+
+ case BX_ATA_MODE_Z_VOLATILE:
+ BX_PANIC(("z-volatile disk support not implemented"));
+ BX_INFO(("HD on ata%d-%d: '%s' 'z-volatile' mode ", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr ()));
+ channels[channel].drives[device].hard_drive = new z_volatile_image_t(disk_size,
+ bx_options.atadevice[channel][device].Ojournal->getptr());
+ break;
+#endif //BX_COMPRESSED_HD_SUPPORT
+#endif
+
+ default:
+ BX_PANIC(("HD on ata%d-%d: '%s' unsupported HD mode : %s", channel, device,
+ bx_options.atadevice[channel][device].Opath->getptr (),
+ atadevice_mode_names[bx_options.atadevice[channel][device].Omode->get()]));
+ break;
+ }
+
+ BX_HD_THIS channels[channel].drives[device].hard_drive->cylinders = cyl;
+ BX_HD_THIS channels[channel].drives[device].hard_drive->heads = heads;
+ BX_HD_THIS channels[channel].drives[device].hard_drive->sectors = spt;
+
+ if (cyl == 0 || heads == 0 || spt == 0) {
+ BX_PANIC(("ata%d/%d cannot have zero cylinders, heads, or sectors/track", channel, device));
+ }
+
+ /* open hard drive image file */
+ if ((BX_HD_THIS channels[channel].drives[device].hard_drive->open(bx_options.atadevice[channel][device].Opath->getptr ())) < 0) {
+ BX_PANIC(("ata%d-%d: could not open hard drive image file '%s'", channel, device, bx_options.atadevice[channel][device].Opath->getptr ()));
+ }
+ }
+ else if (bx_options.atadevice[channel][device].Otype->get() == BX_ATA_DEVICE_CDROM) {
+ BX_DEBUG(( "CDROM on target %d/%d",channel,device));
+ BX_HD_THIS channels[channel].drives[device].device_type = IDE_CDROM;
+ BX_HD_THIS channels[channel].drives[device].cdrom.locked = 0;
+ BX_HD_THIS channels[channel].drives[device].sense.sense_key = SENSE_NONE;
+ BX_HD_THIS channels[channel].drives[device].sense.asc = 0;
+ BX_HD_THIS channels[channel].drives[device].sense.ascq = 0;
+
+ // Check bit fields
+ BX_CONTROLLER(channel,device).sector_count = 0;
+ BX_CONTROLLER(channel,device).interrupt_reason.c_d = 1;
+ if (BX_CONTROLLER(channel,device).sector_count != 0x01)
+ BX_PANIC(("interrupt reason bit field error"));
+
+ BX_CONTROLLER(channel,device).sector_count = 0;
+ BX_CONTROLLER(channel,device).interrupt_reason.i_o = 1;
+ if (BX_CONTROLLER(channel,device).sector_count != 0x02)
+ BX_PANIC(("interrupt reason bit field error"));
+
+ BX_CONTROLLER(channel,device).sector_count = 0;
+ BX_CONTROLLER(channel,device).interrupt_reason.rel = 1;
+ if (BX_CONTROLLER(channel,device).sector_count != 0x04)
+ BX_PANIC(("interrupt reason bit field error"));
+
+ BX_CONTROLLER(channel,device).sector_count = 0;
+ BX_CONTROLLER(channel,device).interrupt_reason.tag = 3;
+ if (BX_CONTROLLER(channel,device).sector_count != 0x18)
+ BX_PANIC(("interrupt reason bit field error"));
+ BX_CONTROLLER(channel,device).sector_count = 0;
+
+ // allocate low level driver
+#ifdef LOWLEVEL_CDROM
+ BX_HD_THIS channels[channel].drives[device].cdrom.cd = new LOWLEVEL_CDROM(bx_options.atadevice[channel][device].Opath->getptr ());
+ BX_INFO(("CD on ata%d-%d: '%s'",channel, device, bx_options.atadevice[channel][device].Opath->getptr ()));
+
+ if (bx_options.atadevice[channel][device].Ostatus->get () == BX_INSERTED) {
+ if (BX_HD_THIS channels[channel].drives[device].cdrom.cd->insert_cdrom()) {
+ BX_INFO(( "Media present in CD-ROM drive"));
+ BX_HD_THIS channels[channel].drives[device].cdrom.ready = 1;
+ BX_HD_THIS channels[channel].drives[device].cdrom.capacity = BX_HD_THIS channels[channel].drives[device].cdrom.cd->capacity();
+ } else {
+ BX_INFO(( "Could not locate CD-ROM, continuing with media not present"));
+ BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0;
+ bx_options.atadevice[channel][device].Ostatus->set(BX_EJECTED);
+ }
+ } else {
+#endif
+ BX_INFO(( "Media not present in CD-ROM drive" ));
+ BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0;
+#ifdef LOWLEVEL_CDROM
+ }
+#endif
+ }
+ }
+ }
+
+#if BX_PDC20230C_VLBIDE_SUPPORT
+ BX_HD_THIS pdc20230c.prog_mode = 0;
+ BX_HD_THIS pdc20230c.prog_count = 0;
+ BX_HD_THIS pdc20230c.p1f3_value = 0;
+ BX_HD_THIS pdc20230c.p1f4_value = 0;
+#endif
+
+
+ // generate CMOS values for hard drive if not using a CMOS image
+ if (!bx_options.cmos.OcmosImage->get ()) {
+ DEV_cmos_set_reg(0x12, 0x00); // start out with: no drive 0, no drive 1
+
+ if (BX_DRIVE_IS_HD(0,0)) {
+ // Flag drive type as Fh, use extended CMOS location as real type
+ DEV_cmos_set_reg(0x12, (DEV_cmos_get_reg(0x12) & 0x0f) | 0xf0);
+ DEV_cmos_set_reg(0x19, 47); // user definable type
+ // AMI BIOS: 1st hard disk #cyl low byte
+ DEV_cmos_set_reg(0x1b, (bx_options.atadevice[0][0].Ocylinders->get () & 0x00ff));
+ // AMI BIOS: 1st hard disk #cyl high byte
+ DEV_cmos_set_reg(0x1c, (bx_options.atadevice[0][0].Ocylinders->get () & 0xff00) >> 8);
+ // AMI BIOS: 1st hard disk #heads
+ DEV_cmos_set_reg(0x1d, (bx_options.atadevice[0][0].Oheads->get ()));
+ // AMI BIOS: 1st hard disk write precompensation cylinder, low byte
+ DEV_cmos_set_reg(0x1e, 0xff); // -1
+ // AMI BIOS: 1st hard disk write precompensation cylinder, high byte
+ DEV_cmos_set_reg(0x1f, 0xff); // -1
+ // AMI BIOS: 1st hard disk control byte
+ DEV_cmos_set_reg(0x20, (0xc0 | ((bx_options.atadevice[0][0].Oheads->get () > 8) << 3)));
+ // AMI BIOS: 1st hard disk landing zone, low byte
+ DEV_cmos_set_reg(0x21, DEV_cmos_get_reg(0x1b));
+ // AMI BIOS: 1st hard disk landing zone, high byte
+ DEV_cmos_set_reg(0x22, DEV_cmos_get_reg(0x1c));
+ // AMI BIOS: 1st hard disk sectors/track
+ DEV_cmos_set_reg(0x23, bx_options.atadevice[0][0].Ospt->get ());
+ }
+
+ //set up cmos for second hard drive
+ if (BX_DRIVE_IS_HD(0,1)) {
+ BX_DEBUG(("1: I will put 0xf into the second hard disk field"));
+ // fill in lower 4 bits of 0x12 for second HD
+ DEV_cmos_set_reg(0x12, (DEV_cmos_get_reg(0x12) & 0xf0) | 0x0f);
+ DEV_cmos_set_reg(0x1a, 47); // user definable type
+ // AMI BIOS: 2nd hard disk #cyl low byte
+ DEV_cmos_set_reg(0x24, (bx_options.atadevice[0][1].Ocylinders->get () & 0x00ff));
+ // AMI BIOS: 2nd hard disk #cyl high byte
+ DEV_cmos_set_reg(0x25, (bx_options.atadevice[0][1].Ocylinders->get () & 0xff00) >> 8);
+ // AMI BIOS: 2nd hard disk #heads
+ DEV_cmos_set_reg(0x26, (bx_options.atadevice[0][1].Oheads->get ()));
+ // AMI BIOS: 2nd hard disk write precompensation cylinder, low byte
+ DEV_cmos_set_reg(0x27, 0xff); // -1
+ // AMI BIOS: 2nd hard disk write precompensation cylinder, high byte
+ DEV_cmos_set_reg(0x28, 0xff); // -1
+ // AMI BIOS: 2nd hard disk, 0x80 if heads>8
+ DEV_cmos_set_reg(0x29, (bx_options.atadevice[0][1].Oheads->get () > 8) ? 0x80 : 0x00);
+ // AMI BIOS: 2nd hard disk landing zone, low byte
+ DEV_cmos_set_reg(0x2a, DEV_cmos_get_reg(0x24));
+ // AMI BIOS: 2nd hard disk landing zone, high byte
+ DEV_cmos_set_reg(0x2b, DEV_cmos_get_reg(0x25));
+ // AMI BIOS: 2nd hard disk sectors/track
+ DEV_cmos_set_reg(0x2c, bx_options.atadevice[0][1].Ospt->get ());
+ }
+
+ DEV_cmos_set_reg(0x39, 0);
+ DEV_cmos_set_reg(0x3a, 0);
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ for (Bit8u device=0; device<2; device ++) {
+ if (bx_options.atadevice[channel][device].Opresent->get()) {
+ if (BX_DRIVE_IS_HD(channel,device)) {
+ Bit16u cylinders = bx_options.atadevice[channel][device].Ocylinders->get();
+ Bit16u heads = bx_options.atadevice[channel][device].Oheads->get();
+ Bit16u spt = bx_options.atadevice[channel][device].Ospt->get();
+ Bit8u translation = bx_options.atadevice[channel][device].Otranslation->get();
+
+ Bit8u reg = 0x39 + channel/2;
+ Bit8u bitshift = 2 * (device+(2 * (channel%2)));
+
+ // Find the right translation if autodetect
+ if (translation == BX_ATA_TRANSLATION_AUTO) {
+ if((cylinders <= 1024) && (heads <= 16) && (spt <= 63)) {
+ translation = BX_ATA_TRANSLATION_NONE;
+ }
+ else if (((Bit32u)cylinders * (Bit32u)heads) <= 131072) {
+ translation = BX_ATA_TRANSLATION_LARGE;
+ }
+ else translation = BX_ATA_TRANSLATION_LBA;
+
+ BX_INFO(("translation on ata%d-%d set to '%s'",channel, device,
+ translation==BX_ATA_TRANSLATION_NONE?"none":
+ translation==BX_ATA_TRANSLATION_LARGE?"large":
+ "lba"));
+ }
+
+ // FIXME we should test and warn
+ // - if LBA and spt != 63
+ // - if RECHS and heads != 16
+ // - if NONE and size > 1024*16*SPT blocks
+ // - if LARGE and size > 8192*16*SPT blocks
+ // - if RECHS and size > 1024*240*SPT blocks
+ // - if LBA and size > 1024*255*63, not that we can do much about it
+
+ switch(translation) {
+ case BX_ATA_TRANSLATION_NONE:
+ DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (0 << bitshift));
+ break;
+ case BX_ATA_TRANSLATION_LBA:
+ DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (1 << bitshift));
+ break;
+ case BX_ATA_TRANSLATION_LARGE:
+ DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (2 << bitshift));
+ break;
+ case BX_ATA_TRANSLATION_RECHS:
+ DEV_cmos_set_reg(reg, DEV_cmos_get_reg(reg) | (3 << bitshift));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Set the "non-extended" boot device. This will default to DISKC if cdrom
+ if ( bx_options.Obootdrive->get () != BX_BOOT_FLOPPYA) {
+ // system boot sequence C:, A:
+ DEV_cmos_set_reg(0x2d, DEV_cmos_get_reg(0x2d) & 0xdf);
+ }
+ else { // 'a'
+ // system boot sequence A:, C:
+ DEV_cmos_set_reg(0x2d, DEV_cmos_get_reg(0x2d) | 0x20);
+ }
+
+ // Set the "extended" boot device, byte 0x3D (needed for cdrom booting)
+ if ( bx_options.Obootdrive->get () == BX_BOOT_FLOPPYA) {
+ // system boot sequence A:
+ DEV_cmos_set_reg(0x3d, 0x01);
+ BX_INFO(("Boot device will be 'a'"));
+ }
+ else if ( bx_options.Obootdrive->get () == BX_BOOT_DISKC) {
+ // system boot sequence C:
+ DEV_cmos_set_reg(0x3d, 0x02);
+ BX_INFO(("Boot device will be 'c'"));
+ }
+ else if ( bx_options.Obootdrive->get () == BX_BOOT_CDROM) {
+ // system boot sequence cdrom
+ DEV_cmos_set_reg(0x3d, 0x03);
+ BX_INFO(("Boot device will be 'cdrom'"));
+ }
+
+ // Set the signature check flag in cmos, inverted for compatibility
+ DEV_cmos_set_reg(0x38, bx_options.OfloppySigCheck->get());
+ BX_INFO(("Floppy boot signature check is %sabled", bx_options.OfloppySigCheck->get() ? "dis" : "en"));
+ }
+
+}
+
+ void
+bx_hard_drive_c::reset(unsigned type)
+{
+ for (unsigned channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if (BX_HD_THIS channels[channel].irq)
+ DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq);
+ }
+}
+
+
+#define GOTO_RETURN_VALUE if(io_len==4){\
+ goto return_value32;\
+ }\
+ else if(io_len==2){\
+ value16=(Bit16u)value32;\
+ goto return_value16;\
+ }\
+ else{\
+ value8=(Bit8u)value32;\
+ goto return_value8;\
+ }
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_hard_drive_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_HD_SMF
+ bx_hard_drive_c *class_ptr = (bx_hard_drive_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_hard_drive_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_HD_SMF
+ Bit8u value8;
+ Bit16u value16;
+ Bit32u value32;
+
+ Bit8u channel = BX_MAX_ATA_CHANNEL;
+ Bit32u port = 0xff; // undefined
+
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr1) {
+ port = address - BX_HD_THIS channels[channel].ioaddr1;
+ break;
+ }
+ else if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr2) {
+ port = address - BX_HD_THIS channels[channel].ioaddr2 + 0x10;
+ break;
+ }
+ }
+
+ if (channel == BX_MAX_ATA_CHANNEL) {
+ if ((address < 0x03f6) || (address > 0x03f7)) {
+ BX_PANIC(("read: unable to find ATA channel, ioport=0x%04x", address));
+ } else {
+ channel = 0;
+ port = address - 0x03e0;
+ }
+ }
+
+#if BX_PDC20230C_VLBIDE_SUPPORT
+// pdc20230c is only available for first ata channel
+if (channel == 0) {
+
+ // Detect the switch to programming mode
+ if (!BX_HD_THIS pdc20230c.prog_mode) {
+ switch (port) {
+ case 0x02:
+ if ((BX_HD_THIS pdc20230c.prog_count == 0) || (BX_HD_THIS pdc20230c.prog_count > 2)) {
+ BX_HD_THIS pdc20230c.prog_count++;
+ }
+ else {
+ BX_HD_THIS pdc20230c.prog_count=0;
+ }
+ break;
+ case 0x16:
+ if ((BX_HD_THIS pdc20230c.prog_count == 1) || (BX_HD_THIS pdc20230c.prog_count == 2)) {
+ BX_HD_THIS pdc20230c.prog_count++;
+ }
+ else {
+ BX_HD_THIS pdc20230c.prog_count=0;
+ }
+ break;
+ default:
+ BX_HD_THIS pdc20230c.prog_count=0;
+ }
+
+ if (BX_HD_THIS pdc20230c.prog_count == 5) {
+ BX_HD_THIS pdc20230c.prog_mode = 1;
+ BX_SELECTED_CONTROLLER(channel).sector_count &= 0x7f;
+ BX_INFO(("Promise VLB-IDE DC2300: Switching to Programming mode"));
+ }
+ }
+
+ // Returns value when in programming mode
+ if (BX_HD_THIS pdc20230c.prog_mode) {
+ switch (port) {
+ case 0x05:
+ // Leave programming mode
+ BX_HD_THIS pdc20230c.prog_mode = 0;
+ BX_INFO(("Promise VLB-IDE DC2300: Leaving Programming mode"));
+ // Value will be sent be normal code
+ break;
+ case 0x03:
+ // Special programming register
+ value32 = BX_HD_THIS pdc20230c.p1f3_value;
+ GOTO_RETURN_VALUE ;
+ break;
+ case 0x04:
+ // Special programming register
+ value32 = BX_HD_THIS pdc20230c.p1f4_value;
+ GOTO_RETURN_VALUE ;
+ break;
+ }
+ }
+}
+#endif
+
+ switch (port) {
+ case 0x00: // hard disk data (16bit) 0x1f0
+ if (BX_SELECTED_CONTROLLER(channel).status.drq == 0) {
+ BX_ERROR(("IO read(0x%04x) with drq == 0: last command was %02xh",
+ address, (unsigned) BX_SELECTED_CONTROLLER(channel).current_command));
+ return(0);
+ }
+ BX_DEBUG(("IO read(0x%04x): current command is %02xh",
+ address, (unsigned) BX_SELECTED_CONTROLLER(channel).current_command));
+ switch (BX_SELECTED_CONTROLLER(channel).current_command) {
+ case 0x20: // READ SECTORS, with retries
+ case 0x21: // READ SECTORS, without retries
+ if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512)
+ BX_PANIC(("IO read(0x%04x): buffer_index >= 512", address));
+
+#if BX_SupportRepeatSpeedups
+ if (DEV_bulk_io_quantum_requested()) {
+ unsigned transferLen, quantumsMax;
+
+ quantumsMax =
+ (512 - BX_SELECTED_CONTROLLER(channel).buffer_index) / io_len;
+ if ( quantumsMax == 0)
+ BX_PANIC(("IO read(0x%04x): not enough space for read", address));
+ DEV_bulk_io_quantum_transferred() =
+ DEV_bulk_io_quantum_requested();
+ if (quantumsMax < DEV_bulk_io_quantum_transferred())
+ DEV_bulk_io_quantum_transferred() = quantumsMax;
+ transferLen = io_len * DEV_bulk_io_quantum_transferred();
+ memcpy((Bit8u*) DEV_bulk_io_host_addr(),
+ &BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index],
+ transferLen);
+ DEV_bulk_io_host_addr() += transferLen;
+ BX_SELECTED_CONTROLLER(channel).buffer_index += transferLen;
+ value32 = 0; // Value returned not important;
+ }
+ else
+#endif
+ {
+ value32 = 0L;
+ switch(io_len){
+ case 4:
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+3] << 24);
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+2] << 16);
+ case 2:
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+1] << 8);
+ value32 |= BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index];
+ }
+ BX_SELECTED_CONTROLLER(channel).buffer_index += io_len;
+ }
+
+ // if buffer completely read
+ if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) {
+ // update sector count, sector number, cylinder,
+ // drive, head, status
+ // if there are more sectors, read next one in...
+ //
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+
+ increment_address(channel);
+
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ if (bx_options.OnewHardDriveSupport->get ())
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ else
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 0;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ if (BX_SELECTED_CONTROLLER(channel).sector_count==0) {
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ }
+ else { /* read next one into controller buffer */
+ off_t logical_sector;
+ off_t ret;
+
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+
+#if TEST_READ_BEYOND_END==1
+ BX_SELECTED_CONTROLLER(channel).cylinder_no += 100000;
+#endif
+ if (!calculate_logical_address(channel, &logical_sector)) {
+ BX_ERROR(("multi-sector read reached invalid sector %lu, aborting", (unsigned long)logical_sector));
+ command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command);
+ GOTO_RETURN_VALUE ;
+ }
+ ret = BX_SELECTED_DRIVE(channel).hard_drive->lseek(logical_sector * 512, SEEK_SET);
+ if (ret < 0) {
+ BX_ERROR(("could not lseek() hard drive image file"));
+ command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command);
+ GOTO_RETURN_VALUE ;
+ }
+ ret = BX_SELECTED_DRIVE(channel).hard_drive->read((bx_ptr_t) BX_SELECTED_CONTROLLER(channel).buffer, 512);
+ if (ret < 512) {
+ BX_ERROR(("logical sector was %lu", (unsigned long)logical_sector));
+ BX_ERROR(("could not read() hard drive image file at byte %lu", (unsigned long)logical_sector*512));
+ command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command);
+ GOTO_RETURN_VALUE ;
+ }
+
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ raise_interrupt(channel);
+ }
+ }
+ GOTO_RETURN_VALUE ;
+ break;
+
+ case 0xec: // IDENTIFY DEVICE
+ case 0xa1:
+ if (bx_options.OnewHardDriveSupport->get ()) {
+ unsigned index;
+
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ index = BX_SELECTED_CONTROLLER(channel).buffer_index;
+ value32 = BX_SELECTED_CONTROLLER(channel).buffer[index];
+ index++;
+ if (io_len >= 2) {
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index] << 8);
+ index++;
+ }
+ if (io_len == 4) {
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index] << 16);
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+1] << 24);
+ index += 2;
+ }
+ BX_SELECTED_CONTROLLER(channel).buffer_index = index;
+
+ if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) {
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("Read all drive ID Bytes ..."));
+ }
+ GOTO_RETURN_VALUE;
+ }
+ else
+ BX_PANIC(("IO read(0x%04x): current command is %02xh", address,
+ (unsigned) BX_SELECTED_CONTROLLER(channel).current_command));
+
+ case 0xa0: {
+ unsigned index = BX_SELECTED_CONTROLLER(channel).buffer_index;
+ unsigned increment = 0;
+
+ // Load block if necessary
+ if (index >= 2048) {
+ if (index > 2048)
+ BX_PANIC(("index > 2048 : 0x%x",index));
+ switch (BX_SELECTED_DRIVE(channel).atapi.command) {
+ case 0x28: // read (10)
+ case 0xa8: // read (12)
+#ifdef LOWLEVEL_CDROM
+ if (!BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ BX_PANIC(("Read with CDROM not ready"));
+ }
+ BX_SELECTED_DRIVE(channel).cdrom.cd->read_block(BX_SELECTED_CONTROLLER(channel).buffer,
+ BX_SELECTED_DRIVE(channel).cdrom.next_lba);
+ BX_SELECTED_DRIVE(channel).cdrom.next_lba++;
+ BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks--;
+
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ if (!BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks)
+ BX_INFO(("Last READ block loaded {CDROM}"));
+ else
+ BX_INFO(("READ block loaded (%d remaining) {CDROM}",
+ BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks));
+
+ // one block transfered, start at beginning
+ index = 0;
+#else
+ BX_PANIC(("Read with no LOWLEVEL_CDROM"));
+#endif
+ break;
+
+ default: // no need to load a new block
+ break;
+ }
+ }
+
+ value32 = BX_SELECTED_CONTROLLER(channel).buffer[index+increment];
+ increment++;
+ if (io_len >= 2) {
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+increment] << 8);
+ increment++;
+ }
+ if (io_len == 4) {
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+increment] << 16);
+ value32 |= (BX_SELECTED_CONTROLLER(channel).buffer[index+increment+1] << 24);
+ increment += 2;
+ }
+ BX_SELECTED_CONTROLLER(channel).buffer_index = index + increment;
+ BX_SELECTED_CONTROLLER(channel).drq_index += increment;
+
+ if (BX_SELECTED_CONTROLLER(channel).drq_index >= (unsigned)BX_SELECTED_DRIVE(channel).atapi.drq_bytes) {
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).drq_index = 0;
+
+ BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining -= BX_SELECTED_DRIVE(channel).atapi.drq_bytes;
+
+ if (BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining > 0) {
+ // one or more blocks remaining (works only for single block commands)
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("PACKET drq bytes read"));
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 0;
+
+ // set new byte count if last block
+ if (BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining < BX_SELECTED_CONTROLLER(channel).byte_count) {
+ BX_SELECTED_CONTROLLER(channel).byte_count = BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining;
+ }
+ BX_SELECTED_DRIVE(channel).atapi.drq_bytes = BX_SELECTED_CONTROLLER(channel).byte_count;
+
+ raise_interrupt(channel);
+ } else {
+ // all bytes read
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("PACKET all bytes read"));
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ raise_interrupt(channel);
+ }
+ }
+ GOTO_RETURN_VALUE;
+ break;
+ }
+
+ // List all the read operations that are defined in the ATA/ATAPI spec
+ // that we don't support. Commands that are listed here will cause a
+ // BX_ERROR, which is non-fatal, and the command will be aborted.
+ case 0x08: BX_ERROR(("read cmd 0x08 (DEVICE RESET) not supported")); command_aborted(channel, 0x08); break;
+ case 0x10: BX_ERROR(("read cmd 0x10 (RECALIBRATE) not supported")); command_aborted(channel, 0x10); break;
+ case 0x22: BX_ERROR(("read cmd 0x22 (READ LONG) not supported")); command_aborted(channel, 0x22); break;
+ case 0x23: BX_ERROR(("read cmd 0x23 (READ LONG NO RETRY) not supported")); command_aborted(channel, 0x23); break;
+ case 0x24: BX_ERROR(("read cmd 0x24 (READ SECTORS EXT) not supported")); command_aborted(channel, 0x24); break;
+ case 0x25: BX_ERROR(("read cmd 0x25 (READ DMA EXT) not supported")); command_aborted(channel, 0x25); break;
+ case 0x26: BX_ERROR(("read cmd 0x26 (READ DMA QUEUED EXT) not supported")); command_aborted(channel, 0x26); break;
+ case 0x27: BX_ERROR(("read cmd 0x27 (READ NATIVE MAX ADDRESS EXT) not supported")); command_aborted(channel, 0x27); break;
+ case 0x29: BX_ERROR(("read cmd 0x29 (READ MULTIPLE EXT) not supported")); command_aborted(channel, 0x29); break;
+ case 0x2A: BX_ERROR(("read cmd 0x2A (READ STREAM DMA) not supported")); command_aborted(channel, 0x2A); break;
+ case 0x2B: BX_ERROR(("read cmd 0x2B (READ STREAM PIO) not supported")); command_aborted(channel, 0x2B); break;
+ case 0x2F: BX_ERROR(("read cmd 0x2F (READ LOG EXT) not supported")); command_aborted(channel, 0x2F); break;
+ case 0x30: BX_ERROR(("read cmd 0x30 (WRITE SECTORS) not supported")); command_aborted(channel, 0x30); break;
+ case 0x31: BX_ERROR(("read cmd 0x31 (WRITE SECTORS NO RETRY) not supported")); command_aborted(channel, 0x31); break;
+ case 0x32: BX_ERROR(("read cmd 0x32 (WRITE LONG) not supported")); command_aborted(channel, 0x32); break;
+ case 0x33: BX_ERROR(("read cmd 0x33 (WRITE LONG NO RETRY) not supported")); command_aborted(channel, 0x33); break;
+ case 0x34: BX_ERROR(("read cmd 0x34 (WRITE SECTORS EXT) not supported")); command_aborted(channel, 0x34); break;
+ case 0x35: BX_ERROR(("read cmd 0x35 (WRITE DMA EXT) not supported")); command_aborted(channel, 0x35); break;
+ case 0x36: BX_ERROR(("read cmd 0x36 (WRITE DMA QUEUED EXT) not supported")); command_aborted(channel, 0x36); break;
+ case 0x37: BX_ERROR(("read cmd 0x37 (SET MAX ADDRESS EXT) not supported")); command_aborted(channel, 0x37); break;
+ case 0x38: BX_ERROR(("read cmd 0x38 (CFA WRITE SECTORS W/OUT ERASE) not supported")); command_aborted(channel, 0x38); break;
+ case 0x39: BX_ERROR(("read cmd 0x39 (WRITE MULTIPLE EXT) not supported")); command_aborted(channel, 0x39); break;
+ case 0x3A: BX_ERROR(("read cmd 0x3A (WRITE STREAM DMA) not supported")); command_aborted(channel, 0x3A); break;
+ case 0x3B: BX_ERROR(("read cmd 0x3B (WRITE STREAM PIO) not supported")); command_aborted(channel, 0x3B); break;
+ case 0x3F: BX_ERROR(("read cmd 0x3F (WRITE LOG EXT) not supported")); command_aborted(channel, 0x3F); break;
+ case 0x40: BX_ERROR(("read cmd 0x40 (READ VERIFY SECTORS) not supported")); command_aborted(channel, 0x40); break;
+ case 0x41: BX_ERROR(("read cmd 0x41 (READ VERIFY SECTORS NO RETRY) not supported")); command_aborted(channel, 0x41); break;
+ case 0x42: BX_ERROR(("read cmd 0x42 (READ VERIFY SECTORS EXT) not supported")); command_aborted(channel, 0x42); break;
+ case 0x50: BX_ERROR(("read cmd 0x50 (FORMAT TRACK) not supported")); command_aborted(channel, 0x50); break;
+ case 0x51: BX_ERROR(("read cmd 0x51 (CONFIGURE STREAM) not supported")); command_aborted(channel, 0x51); break;
+ case 0x70: BX_ERROR(("read cmd 0x70 (SEEK) not supported")); command_aborted(channel, 0x70); break;
+ case 0x87: BX_ERROR(("read cmd 0x87 (CFA TRANSLATE SECTOR) not supported")); command_aborted(channel, 0x87); break;
+ case 0x90: BX_ERROR(("read cmd 0x90 (EXECUTE DEVICE DIAGNOSTIC) not supported")); command_aborted(channel, 0x90); break;
+ case 0x91: BX_ERROR(("read cmd 0x91 (INITIALIZE DEVICE PARAMETERS) not supported")); command_aborted(channel, 0x91); break;
+ case 0x92: BX_ERROR(("read cmd 0x92 (DOWNLOAD MICROCODE) not supported")); command_aborted(channel, 0x92); break;
+ case 0x94: BX_ERROR(("read cmd 0x94 (STANDBY IMMEDIATE) not supported")); command_aborted(channel, 0x94); break;
+ case 0x95: BX_ERROR(("read cmd 0x95 (IDLE IMMEDIATE) not supported")); command_aborted(channel, 0x95); break;
+ case 0x96: BX_ERROR(("read cmd 0x96 (STANDBY) not supported")); command_aborted(channel, 0x96); break;
+ case 0x97: BX_ERROR(("read cmd 0x97 (IDLE) not supported")); command_aborted(channel, 0x97); break;
+ case 0x98: BX_ERROR(("read cmd 0x98 (CHECK POWER MODE) not supported")); command_aborted(channel, 0x98); break;
+ case 0x99: BX_ERROR(("read cmd 0x99 (SLEEP) not supported")); command_aborted(channel, 0x99); break;
+ case 0xA2: BX_ERROR(("read cmd 0xA2 (SERVICE) not supported")); command_aborted(channel, 0xA2); break;
+ case 0xB0: BX_ERROR(("read cmd 0xB0 (SMART DISABLE OPERATIONS) not supported")); command_aborted(channel, 0xB0); break;
+ case 0xB1: BX_ERROR(("read cmd 0xB1 (DEVICE CONFIGURATION FREEZE LOCK) not supported")); command_aborted(channel, 0xB1); break;
+ case 0xC0: BX_ERROR(("read cmd 0xC0 (CFA ERASE SECTORS) not supported")); command_aborted(channel, 0xC0); break;
+ case 0xC4: BX_ERROR(("read cmd 0xC4 (READ MULTIPLE) not supported")); command_aborted(channel, 0xC4); break;
+ case 0xC5: BX_ERROR(("read cmd 0xC5 (WRITE MULTIPLE) not supported")); command_aborted(channel, 0xC5); break;
+ case 0xC6: BX_ERROR(("read cmd 0xC6 (SET MULTIPLE MODE) not supported")); command_aborted(channel, 0xC6); break;
+ case 0xC7: BX_ERROR(("read cmd 0xC7 (READ DMA QUEUED) not supported")); command_aborted(channel, 0xC7); break;
+ case 0xC8: BX_ERROR(("read cmd 0xC8 (READ DMA) not supported")); command_aborted(channel, 0xC8); break;
+ case 0xC9: BX_ERROR(("read cmd 0xC9 (READ DMA NO RETRY) not supported")); command_aborted(channel, 0xC9); break;
+ case 0xCA: BX_ERROR(("read cmd 0xCA (WRITE DMA) not supported")); command_aborted(channel, 0xCA); break;
+ case 0xCC: BX_ERROR(("read cmd 0xCC (WRITE DMA QUEUED) not supported")); command_aborted(channel, 0xCC); break;
+ case 0xCD: BX_ERROR(("read cmd 0xCD (CFA WRITE MULTIPLE W/OUT ERASE) not supported")); command_aborted(channel, 0xCD); break;
+ case 0xD1: BX_ERROR(("read cmd 0xD1 (CHECK MEDIA CARD TYPE) not supported")); command_aborted(channel, 0xD1); break;
+ case 0xDA: BX_ERROR(("read cmd 0xDA (GET MEDIA STATUS) not supported")); command_aborted(channel, 0xDA); break;
+ case 0xDE: BX_ERROR(("read cmd 0xDE (MEDIA LOCK) not supported")); command_aborted(channel, 0xDE); break;
+ case 0xDF: BX_ERROR(("read cmd 0xDF (MEDIA UNLOCK) not supported")); command_aborted(channel, 0xDF); break;
+ case 0xE0: BX_ERROR(("read cmd 0xE0 (STANDBY IMMEDIATE) not supported")); command_aborted(channel, 0xE0); break;
+ case 0xE1: BX_ERROR(("read cmd 0xE1 (IDLE IMMEDIATE) not supported")); command_aborted(channel, 0xE1); break;
+ case 0xE2: BX_ERROR(("read cmd 0xE2 (STANDBY) not supported")); command_aborted(channel, 0xE2); break;
+ case 0xE3: BX_ERROR(("read cmd 0xE3 (IDLE) not supported")); command_aborted(channel, 0xE3); break;
+ case 0xE4: BX_ERROR(("read cmd 0xE4 (READ BUFFER) not supported")); command_aborted(channel, 0xE4); break;
+ case 0xE5: BX_ERROR(("read cmd 0xE5 (CHECK POWER MODE) not supported")); command_aborted(channel, 0xE5); break;
+ case 0xE6: BX_ERROR(("read cmd 0xE6 (SLEEP) not supported")); command_aborted(channel, 0xE6); break;
+ case 0xE7: BX_ERROR(("read cmd 0xE7 (FLUSH CACHE) not supported")); command_aborted(channel, 0xE7); break;
+ case 0xE8: BX_ERROR(("read cmd 0xE8 (WRITE BUFFER) not supported")); command_aborted(channel, 0xE8); break;
+ case 0xEA: BX_ERROR(("read cmd 0xEA (FLUSH CACHE EXT) not supported")); command_aborted(channel, 0xEA); break;
+ case 0xED: BX_ERROR(("read cmd 0xED (MEDIA EJECT) not supported")); command_aborted(channel, 0xED); break;
+ case 0xEF: BX_ERROR(("read cmd 0xEF (SET FEATURES) not supported")); command_aborted(channel, 0xEF); break;
+ case 0xF1: BX_ERROR(("read cmd 0xF1 (SECURITY SET PASSWORD) not supported")); command_aborted(channel, 0xF1); break;
+ case 0xF2: BX_ERROR(("read cmd 0xF2 (SECURITY UNLOCK) not supported")); command_aborted(channel, 0xF2); break;
+ case 0xF3: BX_ERROR(("read cmd 0xF3 (SECURITY ERASE PREPARE) not supported")); command_aborted(channel, 0xF3); break;
+ case 0xF4: BX_ERROR(("read cmd 0xF4 (SECURITY ERASE UNIT) not supported")); command_aborted(channel, 0xF4); break;
+ case 0xF5: BX_ERROR(("read cmd 0xF5 (SECURITY FREEZE LOCK) not supported")); command_aborted(channel, 0xF5); break;
+ case 0xF6: BX_ERROR(("read cmd 0xF6 (SECURITY DISABLE PASSWORD) not supported")); command_aborted(channel, 0xF6); break;
+ case 0xF8: BX_ERROR(("read cmd 0xF8 (READ NATIVE MAX ADDRESS) not supported")); command_aborted(channel, 0xF8); break;
+ case 0xF9: BX_ERROR(("read cmd 0xF9 (SET MAX ADDRESS) not supported")); command_aborted(channel, 0xF9); break;
+
+ default:
+ BX_PANIC(("IO read(0x%04x): current command is %02xh", address,
+ (unsigned) BX_SELECTED_CONTROLLER(channel).current_command));
+ }
+ break;
+
+ case 0x01: // hard disk error register 0x1f1
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ value8 = (!BX_SELECTED_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).error_register;
+ goto return_value8;
+ break;
+ case 0x02: // hard disk sector count / interrupt reason 0x1f2
+ value8 = (!BX_SELECTED_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).sector_count;
+ goto return_value8;
+ break;
+ case 0x03: // sector number 0x1f3
+ value8 = (!BX_SELECTED_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).sector_no;
+ goto return_value8;
+ case 0x04: // cylinder low 0x1f4
+ // -- WARNING : On real hardware the controller registers are shared between drives.
+ // So we must respond even if the select device is not present. Some OS uses this fact
+ // to detect the disks.... minix2 for example
+ value8 = (!BX_ANY_IS_PRESENT(channel)) ? 0 : (BX_SELECTED_CONTROLLER(channel).cylinder_no & 0x00ff);
+ goto return_value8;
+ case 0x05: // cylinder high 0x1f5
+ // -- WARNING : On real hardware the controller registers are shared between drives.
+ // So we must respond even if the select device is not present. Some OS uses this fact
+ // to detect the disks.... minix2 for example
+ value8 = (!BX_ANY_IS_PRESENT(channel)) ? 0 : BX_SELECTED_CONTROLLER(channel).cylinder_no >> 8;
+ goto return_value8;
+
+ case 0x06: // hard disk drive and head register 0x1f6
+ // b7 Extended data field for ECC
+ // b6/b5: Used to be sector size. 00=256,01=512,10=1024,11=128
+ // Since 512 was always used, bit 6 was taken to mean LBA mode:
+ // b6 1=LBA mode, 0=CHS mode
+ // b5 1
+ // b4: DRV
+ // b3..0 HD3..HD0
+ value8 = (1 << 7) |
+ ((BX_SELECTED_CONTROLLER(channel).lba_mode>0) << 6) |
+ (1 << 5) | // 01b = 512 sector size
+ (BX_HD_THIS channels[channel].drive_select << 4) |
+ (BX_SELECTED_CONTROLLER(channel).head_no << 0);
+ goto return_value8;
+ break;
+//BX_CONTROLLER(channel,0).lba_mode
+
+ case 0x07: // Hard Disk Status 0x1f7
+ case 0x16: // Hard Disk Alternate Status 0x3f6
+ if (!BX_ANY_IS_PRESENT(channel)) {
+ // (mch) Just return zero for these registers
+ value8 = 0;
+ } else {
+ value8 = (
+ (BX_SELECTED_CONTROLLER(channel).status.busy << 7) |
+ (BX_SELECTED_CONTROLLER(channel).status.drive_ready << 6) |
+ (BX_SELECTED_CONTROLLER(channel).status.write_fault << 5) |
+ (BX_SELECTED_CONTROLLER(channel).status.seek_complete << 4) |
+ (BX_SELECTED_CONTROLLER(channel).status.drq << 3) |
+ (BX_SELECTED_CONTROLLER(channel).status.corrected_data << 2) |
+ (BX_SELECTED_CONTROLLER(channel).status.index_pulse << 1) |
+ (BX_SELECTED_CONTROLLER(channel).status.err) );
+ BX_SELECTED_CONTROLLER(channel).status.index_pulse_count++;
+ BX_SELECTED_CONTROLLER(channel).status.index_pulse = 0;
+ if (BX_SELECTED_CONTROLLER(channel).status.index_pulse_count >= INDEX_PULSE_CYCLE) {
+ BX_SELECTED_CONTROLLER(channel).status.index_pulse = 1;
+ BX_SELECTED_CONTROLLER(channel).status.index_pulse_count = 0;
+ }
+ }
+ if (port == 0x07) {
+ DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq);
+ }
+ goto return_value8;
+ break;
+
+ case 0x17: // Hard Disk Address Register 0x3f7
+ // Obsolete and unsupported register. Not driven by hard
+ // disk controller. Report all 1's. If floppy controller
+ // is handling this address, it will call this function
+ // set/clear D7 (the only bit it handles), then return
+ // the combined value
+ value8 = 0xff;
+ goto return_value8;
+ break;
+
+ default:
+ BX_PANIC(("hard drive: io read to address %x unsupported",
+ (unsigned) address));
+ }
+
+ BX_PANIC(("hard drive: shouldnt get here!"));
+ return(0);
+
+ return_value32:
+ BX_DEBUG(("32-bit read from %04x = %08x {%s}",
+ (unsigned) address, value32, BX_SELECTED_TYPE_STRING(channel)));
+ return value32;
+
+ return_value16:
+ BX_DEBUG(("16-bit read from %04x = %04x {%s}",
+ (unsigned) address, value16, BX_SELECTED_TYPE_STRING(channel)));
+ return value16;
+
+ return_value8:
+ BX_DEBUG(("8-bit read from %04x = %02x {%s}",
+ (unsigned) address, value8, BX_SELECTED_TYPE_STRING(channel)));
+ return value8;
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_hard_drive_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_HD_SMF
+ bx_hard_drive_c *class_ptr = (bx_hard_drive_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_hard_drive_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_HD_SMF
+ off_t logical_sector;
+ off_t ret;
+ bx_bool prev_control_reset;
+
+ Bit8u channel = BX_MAX_ATA_CHANNEL;
+ Bit32u port = 0xff; // undefined
+
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr1) {
+ port = address - BX_HD_THIS channels[channel].ioaddr1;
+ break;
+ }
+ else if ((address & 0xfff8) == BX_HD_THIS channels[channel].ioaddr2) {
+ port = address - BX_HD_THIS channels[channel].ioaddr2 + 0x10;
+ break;
+ }
+ }
+
+ if (channel == BX_MAX_ATA_CHANNEL) {
+ if (address != 0x03f6) {
+ BX_PANIC(("write: unable to find ATA channel, ioport=0x%04x", address));
+ } else {
+ channel = 0;
+ port = address - 0x03e0;
+ }
+ }
+
+#if BX_PDC20230C_VLBIDE_SUPPORT
+// pdc20230c is only available for first ata channel
+if (channel == 0) {
+ BX_HD_THIS pdc20230c.prog_count = 0;
+
+ if (BX_HD_THIS pdc20230c.prog_mode != 0) {
+ switch (port) {
+ case 0x03:
+ BX_HD_THIS pdc20230c.p1f3_value = value;
+ return;
+ break;
+ case 0x04:
+ BX_HD_THIS pdc20230c.p1f4_value = value;
+ return;
+ break;
+ }
+ }
+}
+#endif
+
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) {
+ switch (io_len) {
+ case 1:
+ BX_INFO(("8-bit write to %04x = %02x {%s}",
+ (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+
+ case 2:
+ BX_INFO(("16-bit write to %04x = %04x {%s}",
+ (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+
+ case 4:
+ BX_INFO(("32-bit write to %04x = %08x {%s}",
+ (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+
+ default:
+ BX_INFO(("unknown-size write to %04x = %08x {%s}",
+ (unsigned) address, (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+ }
+ }
+
+ BX_DEBUG(("IO write to %04x = %02x", (unsigned) address, (unsigned) value));
+
+ switch (port) {
+ case 0x00: // 0x1f0
+ switch (BX_SELECTED_CONTROLLER(channel).current_command) {
+ case 0x30: // WRITE SECTORS
+ if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512)
+ BX_PANIC(("IO write(0x%04x): buffer_index >= 512", address));
+
+#if BX_SupportRepeatSpeedups
+ if (DEV_bulk_io_quantum_requested()) {
+ unsigned transferLen, quantumsMax;
+
+ quantumsMax =
+ (512 - BX_SELECTED_CONTROLLER(channel).buffer_index) / io_len;
+ if ( quantumsMax == 0)
+ BX_PANIC(("IO write(0x%04x): not enough space for write", address));
+ DEV_bulk_io_quantum_transferred() =
+ DEV_bulk_io_quantum_requested();
+ if (quantumsMax < DEV_bulk_io_quantum_transferred())
+ DEV_bulk_io_quantum_transferred() = quantumsMax;
+ transferLen = io_len * DEV_bulk_io_quantum_transferred();
+ memcpy(
+ &BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index],
+ (Bit8u*) DEV_bulk_io_host_addr(),
+ transferLen);
+ DEV_bulk_io_host_addr() += transferLen;
+ BX_SELECTED_CONTROLLER(channel).buffer_index += transferLen;
+ }
+ else
+#endif
+ {
+ switch(io_len){
+ case 4:
+ BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+3] = (Bit8u)(value >> 24);
+ BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+2] = (Bit8u)(value >> 16);
+ case 2:
+ BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+1] = (Bit8u)(value >> 8);
+ BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index] = (Bit8u) value;
+ }
+ BX_SELECTED_CONTROLLER(channel).buffer_index += io_len;
+ }
+
+ /* if buffer completely writtten */
+ if (BX_SELECTED_CONTROLLER(channel).buffer_index >= 512) {
+ off_t logical_sector;
+ off_t ret;
+
+#if TEST_WRITE_BEYOND_END==1
+ BX_SELECTED_CONTROLLER(channel).cylinder_no += 100000;
+#endif
+ if (!calculate_logical_address(channel, &logical_sector)) {
+ BX_ERROR(("write reached invalid sector %lu, aborting", (unsigned long)logical_sector));
+ command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command);
+ return;
+ }
+#if TEST_WRITE_BEYOND_END==2
+ logical_sector += 100000;
+#endif
+ ret = BX_SELECTED_DRIVE(channel).hard_drive->lseek(logical_sector * 512, SEEK_SET);
+ if (ret < 0) {
+ BX_ERROR(("could not lseek() hard drive image file at byte %lu", (unsigned long)logical_sector * 512));
+ command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command);
+ return;
+ }
+ ret = BX_SELECTED_DRIVE(channel).hard_drive->write((bx_ptr_t) BX_SELECTED_CONTROLLER(channel).buffer, 512);
+ if (ret < 512) {
+ BX_ERROR(("could not write() hard drive image file at byte %lu", (unsigned long)logical_sector*512));
+ command_aborted (channel, BX_SELECTED_CONTROLLER(channel).current_command);
+ return;
+ }
+
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+
+ /* update sector count, sector number, cylinder,
+ * drive, head, status
+ * if there are more sectors, read next one in...
+ */
+
+ increment_address(channel);
+
+ /* When the write is complete, controller clears the DRQ bit and
+ * sets the BSY bit.
+ * If at least one more sector is to be written, controller sets DRQ bit,
+ * clears BSY bit, and issues IRQ
+ */
+
+ if (BX_SELECTED_CONTROLLER(channel).sector_count!=0) {
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ }
+ else { /* no more sectors to write */
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ }
+ raise_interrupt(channel);
+ }
+ break;
+
+ case 0xa0: // PACKET
+ if (BX_SELECTED_CONTROLLER(channel).buffer_index >= PACKET_SIZE)
+ BX_PANIC(("IO write(0x%04x): buffer_index >= PACKET_SIZE", address));
+ BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index] = value;
+ BX_SELECTED_CONTROLLER(channel).buffer[BX_SELECTED_CONTROLLER(channel).buffer_index+1] = (value >> 8);
+ BX_SELECTED_CONTROLLER(channel).buffer_index += 2;
+
+ /* if packet completely writtten */
+ if (BX_SELECTED_CONTROLLER(channel).buffer_index >= PACKET_SIZE) {
+ // complete command received
+ Bit8u atapi_command = BX_SELECTED_CONTROLLER(channel).buffer[0];
+
+ if (bx_dbg.cdrom)
+ BX_INFO(("cdrom: ATAPI command 0x%x started", atapi_command));
+
+ switch (atapi_command) {
+ case 0x00: // test unit ready
+ if (BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ atapi_cmd_nop(channel);
+ } else {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ }
+ raise_interrupt(channel);
+ break;
+
+ case 0x03: { // request sense
+ int alloc_length = BX_SELECTED_CONTROLLER(channel).buffer[4];
+ init_send_atapi_command(channel, atapi_command, 18, alloc_length);
+
+ // sense data
+ BX_SELECTED_CONTROLLER(channel).buffer[0] = 0x70 | (1 << 7);
+ BX_SELECTED_CONTROLLER(channel).buffer[1] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[2] = BX_SELECTED_DRIVE(channel).sense.sense_key;
+ BX_SELECTED_CONTROLLER(channel).buffer[3] = BX_SELECTED_DRIVE(channel).sense.information.arr[0];
+ BX_SELECTED_CONTROLLER(channel).buffer[4] = BX_SELECTED_DRIVE(channel).sense.information.arr[1];
+ BX_SELECTED_CONTROLLER(channel).buffer[5] = BX_SELECTED_DRIVE(channel).sense.information.arr[2];
+ BX_SELECTED_CONTROLLER(channel).buffer[6] = BX_SELECTED_DRIVE(channel).sense.information.arr[3];
+ BX_SELECTED_CONTROLLER(channel).buffer[7] = 17-7;
+ BX_SELECTED_CONTROLLER(channel).buffer[8] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[0];
+ BX_SELECTED_CONTROLLER(channel).buffer[9] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[1];
+ BX_SELECTED_CONTROLLER(channel).buffer[10] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[2];
+ BX_SELECTED_CONTROLLER(channel).buffer[11] = BX_SELECTED_DRIVE(channel).sense.specific_inf.arr[3];
+ BX_SELECTED_CONTROLLER(channel).buffer[12] = BX_SELECTED_DRIVE(channel).sense.asc;
+ BX_SELECTED_CONTROLLER(channel).buffer[13] = BX_SELECTED_DRIVE(channel).sense.ascq;
+ BX_SELECTED_CONTROLLER(channel).buffer[14] = BX_SELECTED_DRIVE(channel).sense.fruc;
+ BX_SELECTED_CONTROLLER(channel).buffer[15] = BX_SELECTED_DRIVE(channel).sense.key_spec.arr[0];
+ BX_SELECTED_CONTROLLER(channel).buffer[16] = BX_SELECTED_DRIVE(channel).sense.key_spec.arr[1];
+ BX_SELECTED_CONTROLLER(channel).buffer[17] = BX_SELECTED_DRIVE(channel).sense.key_spec.arr[2];
+
+ ready_to_send_atapi(channel);
+ }
+ break;
+
+ case 0x1b: { // start stop unit
+ //bx_bool Immed = (BX_SELECTED_CONTROLLER(channel).buffer[1] >> 0) & 1;
+ bx_bool LoEj = (BX_SELECTED_CONTROLLER(channel).buffer[4] >> 1) & 1;
+ bx_bool Start = (BX_SELECTED_CONTROLLER(channel).buffer[4] >> 0) & 1;
+
+ if (!LoEj && !Start) { // stop the disc
+ BX_ERROR(("FIXME: Stop disc not implemented"));
+ atapi_cmd_nop(channel);
+ raise_interrupt(channel);
+ } else if (!LoEj && Start) { // start (spin up) the disc
+#ifdef LOWLEVEL_CDROM
+ BX_SELECTED_DRIVE(channel).cdrom.cd->start_cdrom();
+#endif
+ BX_ERROR(("FIXME: ATAPI start disc not reading TOC"));
+ atapi_cmd_nop(channel);
+ raise_interrupt(channel);
+ } else if (LoEj && !Start) { // Eject the disc
+ atapi_cmd_nop(channel);
+
+ if (BX_SELECTED_DRIVE(channel).cdrom.ready) {
+#ifdef LOWLEVEL_CDROM
+ BX_SELECTED_DRIVE(channel).cdrom.cd->eject_cdrom();
+#endif
+ BX_SELECTED_DRIVE(channel).cdrom.ready = 0;
+ bx_options.atadevice[channel][BX_SLAVE_SELECTED(channel)].Ostatus->set(BX_EJECTED);
+ bx_gui->update_drive_status_buttons();
+ }
+ raise_interrupt(channel);
+ } else { // Load the disc
+ // My guess is that this command only closes the tray, that's a no-op for us
+ atapi_cmd_nop(channel);
+ raise_interrupt(channel);
+ }
+ }
+ break;
+
+ case 0xbd: { // mechanism status
+ uint16 alloc_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 8);
+
+ if (alloc_length == 0)
+ BX_PANIC(("Zero allocation length to MECHANISM STATUS not impl."));
+
+ init_send_atapi_command(channel, atapi_command, 8, alloc_length);
+
+ BX_SELECTED_CONTROLLER(channel).buffer[0] = 0; // reserved for non changers
+ BX_SELECTED_CONTROLLER(channel).buffer[1] = 0; // reserved for non changers
+
+ BX_SELECTED_CONTROLLER(channel).buffer[2] = 0; // Current LBA (TODO!)
+ BX_SELECTED_CONTROLLER(channel).buffer[3] = 0; // Current LBA (TODO!)
+ BX_SELECTED_CONTROLLER(channel).buffer[4] = 0; // Current LBA (TODO!)
+
+ BX_SELECTED_CONTROLLER(channel).buffer[5] = 1; // one slot
+
+ BX_SELECTED_CONTROLLER(channel).buffer[6] = 0; // slot table length
+ BX_SELECTED_CONTROLLER(channel).buffer[7] = 0; // slot table length
+
+ ready_to_send_atapi(channel);
+ }
+ break;
+
+ case 0x5a: { // mode sense
+ uint16 alloc_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 7);
+
+ Bit8u PC = BX_SELECTED_CONTROLLER(channel).buffer[2] >> 6;
+ Bit8u PageCode = BX_SELECTED_CONTROLLER(channel).buffer[2] & 0x3f;
+
+ switch (PC) {
+ case 0x0: // current values
+ switch (PageCode) {
+ case 0x01: // error recovery
+ init_send_atapi_command(channel, atapi_command, sizeof(error_recovery_t) + 8, alloc_length);
+
+ init_mode_sense_single(channel, &BX_SELECTED_DRIVE(channel).cdrom.current.error_recovery,
+ sizeof(error_recovery_t));
+ ready_to_send_atapi(channel);
+ break;
+
+ case 0x2a: // CD-ROM capabilities & mech. status
+ init_send_atapi_command(channel, atapi_command, 28, alloc_length);
+ init_mode_sense_single(channel, &BX_SELECTED_CONTROLLER(channel).buffer[8], 28);
+ BX_SELECTED_CONTROLLER(channel).buffer[8] = 0x2a;
+ BX_SELECTED_CONTROLLER(channel).buffer[9] = 0x12;
+ BX_SELECTED_CONTROLLER(channel).buffer[10] = 0x00;
+ BX_SELECTED_CONTROLLER(channel).buffer[11] = 0x00;
+ // Multisession, Mode 2 Form 2, Mode 2 Form 1
+ BX_SELECTED_CONTROLLER(channel).buffer[12] = 0x70;
+ BX_SELECTED_CONTROLLER(channel).buffer[13] = (3 << 5);
+ BX_SELECTED_CONTROLLER(channel).buffer[14] = (unsigned char)
+(1 |
+ (BX_SELECTED_DRIVE(channel).cdrom.locked ? (1 << 1) : 0) |
+ (1 << 3) |
+ (1 << 5));
+ BX_SELECTED_CONTROLLER(channel).buffer[15] = 0x00;
+ BX_SELECTED_CONTROLLER(channel).buffer[16] = (706 >> 8) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[17] = 706 & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[18] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[19] = 2;
+ BX_SELECTED_CONTROLLER(channel).buffer[20] = (512 >> 8) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[21] = 512 & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[22] = (706 >> 8) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[23] = 706 & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[24] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[25] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[26] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[27] = 0;
+ ready_to_send_atapi(channel);
+ break;
+
+ case 0x0d: // CD-ROM
+ case 0x0e: // CD-ROM audio control
+ case 0x3f: // all
+ BX_ERROR(("cdrom: MODE SENSE (curr), code=%x"
+ " not implemented yet",
+ PageCode));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ break;
+
+ default:
+ // not implemeted by this device
+ BX_INFO(("cdrom: MODE SENSE PC=%x, PageCode=%x,"
+ " not implemented by device",
+ PC, PageCode));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ break;
+ }
+ break;
+
+ case 0x1: // changeable values
+ switch (PageCode) {
+ case 0x01: // error recovery
+ case 0x0d: // CD-ROM
+ case 0x0e: // CD-ROM audio control
+ case 0x2a: // CD-ROM capabilities & mech. status
+ case 0x3f: // all
+ BX_ERROR(("cdrom: MODE SENSE (chg), code=%x"
+ " not implemented yet",
+ PageCode));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ break;
+
+ default:
+ // not implemeted by this device
+ BX_INFO(("cdrom: MODE SENSE PC=%x, PageCode=%x,"
+ " not implemented by device",
+ PC, PageCode));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ break;
+ }
+ break;
+
+ case 0x2: // default values
+ switch (PageCode) {
+ case 0x01: // error recovery
+ case 0x0d: // CD-ROM
+ case 0x0e: // CD-ROM audio control
+ case 0x2a: // CD-ROM capabilities & mech. status
+ case 0x3f: // all
+ BX_PANIC(("cdrom: MODE SENSE (dflt), code=%x",
+ PageCode));
+ break;
+
+ default:
+ // not implemeted by this device
+ BX_INFO(("cdrom: MODE SENSE PC=%x, PageCode=%x,"
+ " not implemented by device",
+ PC, PageCode));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ break;
+ }
+ break;
+
+ case 0x3: // saved values not implemented
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_SAVING_PARAMETERS_NOT_SUPPORTED);
+ raise_interrupt(channel);
+ break;
+
+ default:
+ BX_PANIC(("Should not get here!"));
+ break;
+ }
+ }
+ break;
+
+ case 0x12: { // inquiry
+ uint8 alloc_length = BX_SELECTED_CONTROLLER(channel).buffer[4];
+
+ init_send_atapi_command(channel, atapi_command, 36, alloc_length);
+
+ BX_SELECTED_CONTROLLER(channel).buffer[0] = 0x05; // CD-ROM
+ BX_SELECTED_CONTROLLER(channel).buffer[1] = 0x80; // Removable
+ BX_SELECTED_CONTROLLER(channel).buffer[2] = 0x00; // ISO, ECMA, ANSI version
+ BX_SELECTED_CONTROLLER(channel).buffer[3] = 0x21; // ATAPI-2, as specified
+ BX_SELECTED_CONTROLLER(channel).buffer[4] = 31; // additional length (total 36)
+ BX_SELECTED_CONTROLLER(channel).buffer[5] = 0x00; // reserved
+ BX_SELECTED_CONTROLLER(channel).buffer[6] = 0x00; // reserved
+ BX_SELECTED_CONTROLLER(channel).buffer[7] = 0x00; // reserved
+
+ // Vendor ID
+ const char* vendor_id = "VTAB ";
+ int i;
+ for (i = 0; i < 8; i++)
+ BX_SELECTED_CONTROLLER(channel).buffer[8+i] = vendor_id[i];
+
+ // Product ID
+ const char* product_id = "Turbo CD-ROM ";
+ for (i = 0; i < 16; i++)
+ BX_SELECTED_CONTROLLER(channel).buffer[16+i] = product_id[i];
+
+ // Product Revision level
+ const char* rev_level = "1.0 ";
+ for (i = 0; i < 4; i++)
+ BX_SELECTED_CONTROLLER(channel).buffer[32+i] = rev_level[i];
+
+ ready_to_send_atapi(channel);
+ }
+ break;
+
+ case 0x25: { // read cd-rom capacity
+ // no allocation length???
+ init_send_atapi_command(channel, atapi_command, 8, 8);
+
+ if (BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ uint32 capacity = BX_SELECTED_DRIVE(channel).cdrom.capacity;
+ BX_INFO(("Capacity is %d sectors (%d bytes)", capacity, capacity * 2048));
+ BX_SELECTED_CONTROLLER(channel).buffer[0] = (capacity >> 24) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[1] = (capacity >> 16) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[2] = (capacity >> 8) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[3] = (capacity >> 0) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[4] = (2048 >> 24) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[5] = (2048 >> 16) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[6] = (2048 >> 8) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[7] = (2048 >> 0) & 0xff;
+ ready_to_send_atapi(channel);
+ } else {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ raise_interrupt(channel);
+ }
+ }
+ break;
+
+ case 0xbe: { // read cd
+ if (BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ BX_ERROR(("Read CD with CD present not implemented"));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ } else {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ raise_interrupt(channel);
+ }
+ }
+ break;
+
+ case 0x43: { // read toc
+ if (BX_SELECTED_DRIVE(channel).cdrom.ready) {
+#ifdef LOWLEVEL_CDROM
+ bool msf = (BX_SELECTED_CONTROLLER(channel).buffer[1] >> 1) & 1;
+ uint8 starting_track = BX_SELECTED_CONTROLLER(channel).buffer[6];
+#endif
+ uint16 alloc_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 7);
+
+ uint8 format = (BX_SELECTED_CONTROLLER(channel).buffer[9] >> 6);
+ int i;
+ switch (format) {
+ case 0:
+#ifdef LOWLEVEL_CDROM
+ int toc_length;
+ if (!(BX_SELECTED_DRIVE(channel).cdrom.cd->read_toc(BX_SELECTED_CONTROLLER(channel).buffer,
+ &toc_length, msf, starting_track))) {
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ } else {
+ init_send_atapi_command(channel, atapi_command, toc_length, alloc_length);
+ ready_to_send_atapi(channel);
+ }
+#else
+ BX_PANIC(("LOWLEVEL_CDROM not defined"));
+#endif
+ break;
+
+ case 1:
+ // multi session stuff. we ignore this and emulate a single session only
+ init_send_atapi_command(channel, atapi_command, 12, alloc_length);
+
+ BX_SELECTED_CONTROLLER(channel).buffer[0] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[1] = 0x0a;
+ BX_SELECTED_CONTROLLER(channel).buffer[2] = 1;
+ BX_SELECTED_CONTROLLER(channel).buffer[3] = 1;
+ for (i = 0; i < 8; i++)
+ BX_SELECTED_CONTROLLER(channel).buffer[4+i] = 0;
+
+ ready_to_send_atapi(channel);
+ break;
+
+ case 2:
+ default:
+ BX_PANIC(("(READ TOC) Format %d not supported", format));
+ break;
+ }
+ } else {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ raise_interrupt(channel);
+ }
+ }
+ break;
+
+ case 0x28: // read (10)
+ case 0xa8: // read (12)
+ {
+
+ uint32 transfer_length;
+ if (atapi_command == 0x28)
+ transfer_length = read_16bit(BX_SELECTED_CONTROLLER(channel).buffer + 7);
+ else
+ transfer_length = read_32bit(BX_SELECTED_CONTROLLER(channel).buffer + 6);
+
+ uint32 lba = read_32bit(BX_SELECTED_CONTROLLER(channel).buffer + 2);
+
+ if (!BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ raise_interrupt(channel);
+ break;
+ }
+
+ if (transfer_length == 0) {
+ atapi_cmd_nop(channel);
+ raise_interrupt(channel);
+ BX_INFO(("READ(%d) with transfer length 0, ok", atapi_command==0x28?10:12));
+ break;
+ }
+
+ if (lba + transfer_length > BX_SELECTED_DRIVE(channel).cdrom.capacity) {
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
+ raise_interrupt(channel);
+ break;
+ }
+
+ BX_DEBUG(("cdrom: READ (%d) LBA=%d LEN=%d", atapi_command==0x28?10:12, lba, transfer_length));
+
+ // handle command
+ init_send_atapi_command(channel, atapi_command, transfer_length * 2048,
+ transfer_length * 2048, true);
+ BX_SELECTED_DRIVE(channel).cdrom.remaining_blocks = transfer_length;
+ BX_SELECTED_DRIVE(channel).cdrom.next_lba = lba;
+ ready_to_send_atapi(channel);
+ }
+ break;
+
+ case 0x2b: { // seek
+ uint32 lba = read_32bit(BX_SELECTED_CONTROLLER(channel).buffer + 2);
+ if (!BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ raise_interrupt(channel);
+ break;
+ }
+
+ if (lba > BX_SELECTED_DRIVE(channel).cdrom.capacity) {
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR);
+ raise_interrupt(channel);
+ break;
+ }
+ BX_INFO(("cdrom: SEEK (ignored)"));
+ atapi_cmd_nop(channel);
+ raise_interrupt(channel);
+ }
+ break;
+
+ case 0x1e: { // prevent/allow medium removal
+ if (BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ BX_SELECTED_DRIVE(channel).cdrom.locked = BX_SELECTED_CONTROLLER(channel).buffer[4] & 1;
+ atapi_cmd_nop(channel);
+ } else {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ }
+ raise_interrupt(channel);
+ }
+ break;
+
+ case 0x42: { // read sub-channel
+ bool msf = get_packet_field(channel,1, 1, 1);
+ bool sub_q = get_packet_field(channel,2, 6, 1);
+ uint8 data_format = get_packet_byte(channel,3);
+ uint8 track_number = get_packet_byte(channel,6);
+ uint16 alloc_length = get_packet_word(channel,7);
+ UNUSED(msf);
+ UNUSED(data_format);
+ UNUSED(track_number);
+
+ if (!BX_SELECTED_DRIVE(channel).cdrom.ready) {
+ atapi_cmd_error(channel, SENSE_NOT_READY, ASC_MEDIUM_NOT_PRESENT);
+ raise_interrupt(channel);
+ } else {
+ BX_SELECTED_CONTROLLER(channel).buffer[0] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[1] = 0; // audio not supported
+ BX_SELECTED_CONTROLLER(channel).buffer[2] = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer[3] = 0;
+
+ int ret_len = 4; // header size
+
+ if (sub_q) { // !sub_q == header only
+ BX_ERROR(("Read sub-channel with SubQ not implemented"));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST,
+ ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ }
+
+ init_send_atapi_command(channel, atapi_command, ret_len, alloc_length);
+ ready_to_send_atapi(channel);
+ }
+ }
+ break;
+
+ case 0x51: { // read disc info
+ // no-op to keep the Linux CD-ROM driver happy
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ }
+ break;
+
+ case 0x55: // mode select
+ case 0xa6: // load/unload cd
+ case 0x4b: // pause/resume
+ case 0x45: // play audio
+ case 0x47: // play audio msf
+ case 0xbc: // play cd
+ case 0xb9: // read cd msf
+ case 0x44: // read header
+ case 0xba: // scan
+ case 0xbb: // set cd speed
+ case 0x4e: // stop play/scan
+ case 0x46: // ???
+ case 0x4a: // ???
+ BX_ERROR(("ATAPI command 0x%x not implemented yet",
+ atapi_command));
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ break;
+ default:
+ BX_PANIC(("Unknown ATAPI command 0x%x (%d)",
+ atapi_command, atapi_command));
+ // We'd better signal the error if the user chose to continue
+ atapi_cmd_error(channel, SENSE_ILLEGAL_REQUEST, ASC_INV_FIELD_IN_CMD_PACKET);
+ raise_interrupt(channel);
+ break;
+ }
+ }
+
+ break;
+
+ default:
+ BX_PANIC(("IO write(0x%04x): current command is %02xh", address,
+ (unsigned) BX_SELECTED_CONTROLLER(channel).current_command));
+ }
+ break;
+
+ case 0x01: // hard disk write precompensation 0x1f1
+ WRITE_FEATURES(channel,value);
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom)) {
+ if (value == 0xff)
+ BX_INFO(("no precompensation {%s}", BX_SELECTED_TYPE_STRING(channel)));
+ else
+ BX_INFO(("precompensation value %02x {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ }
+ break;
+
+ case 0x02: // hard disk sector count 0x1f2
+ WRITE_SECTOR_COUNT(channel,value);
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("sector count = %u {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+
+ case 0x03: // hard disk sector number 0x1f3
+ WRITE_SECTOR_NUMBER(channel,value);
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("sector number = %u {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+
+ case 0x04: // hard disk cylinder low 0x1f4
+ WRITE_CYLINDER_LOW(channel,value);
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("cylinder low = %02xh {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+
+ case 0x05: // hard disk cylinder high 0x1f5
+ WRITE_CYLINDER_HIGH(channel,value);
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("cylinder high = %02xh {%s}", (unsigned) value, BX_SELECTED_TYPE_STRING(channel)));
+ break;
+
+ case 0x06: // hard disk drive and head register 0x1f6
+ // b7 Extended data field for ECC
+ // b6/b5: Used to be sector size. 00=256,01=512,10=1024,11=128
+ // Since 512 was always used, bit 6 was taken to mean LBA mode:
+ // b6 1=LBA mode, 0=CHS mode
+ // b5 1
+ // b4: DRV
+ // b3..0 HD3..HD0
+ {
+ if ( (value & 0xa0) != 0xa0 ) // 1x1xxxxx
+ BX_INFO(("IO write 0x%04x (%02x): not 1x1xxxxxb", address, (unsigned) value));
+ Bit32u drvsel = BX_HD_THIS channels[channel].drive_select = (value >> 4) & 0x01;
+ WRITE_HEAD_NO(channel,value & 0xf);
+ if (BX_SELECTED_CONTROLLER(channel).lba_mode == 0 && ((value >> 6) & 1) == 1)
+ BX_DEBUG(("enabling LBA mode"));
+ WRITE_LBA_MODE(channel,(value >> 6) & 1);
+ if (!BX_SELECTED_IS_PRESENT(channel)) {
+ BX_ERROR (("device set to %d which does not exist",drvsel));
+ BX_SELECTED_CONTROLLER(channel).error_register = 0x04; // aborted
+ BX_SELECTED_CONTROLLER(channel).status.err = 1;
+ }
+ break;
+ }
+
+ case 0x07: // hard disk command 0x1f7
+ // (mch) Writes to the command register with drive_select != 0
+ // are ignored if no secondary device is present
+ if ((BX_SLAVE_SELECTED(channel)) && (!BX_SLAVE_IS_PRESENT(channel)))
+ break;
+ // Writes to the command register clear the IRQ
+ DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq);
+
+ if (BX_SELECTED_CONTROLLER(channel).status.busy)
+ BX_PANIC(("hard disk: command sent, controller BUSY"));
+ if ( (value & 0xf0) == 0x10 )
+ value = 0x10;
+ switch (value) {
+
+ case 0x10: // CALIBRATE DRIVE
+ if (!BX_SELECTED_IS_HD(channel))
+ BX_PANIC(("calibrate drive issued to non-disk"));
+ if (!BX_SELECTED_IS_PRESENT(channel)) {
+ BX_SELECTED_CONTROLLER(channel).error_register = 0x02; // Track 0 not found
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 1;
+ raise_interrupt(channel);
+ BX_INFO(("calibrate drive: disk ata%d-%d not present", channel, BX_SLAVE_SELECTED(channel)));
+ break;
+ }
+
+ /* move head to cylinder 0, issue IRQ */
+ BX_SELECTED_CONTROLLER(channel).error_register = 0;
+ BX_SELECTED_CONTROLLER(channel).cylinder_no = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ raise_interrupt(channel);
+ break;
+
+ case 0x20: // READ MULTIPLE SECTORS, with retries
+ case 0x21: // READ MULTIPLE SECTORS, without retries
+ /* update sector_no, always points to current sector
+ * after each sector is read to buffer, DRQ bit set and issue IRQ
+ * if interrupt handler transfers all data words into main memory,
+ * and more sectors to read, then set BSY bit again, clear DRQ and
+ * read next sector into buffer
+ * sector count of 0 means 256 sectors
+ */
+
+ if (!BX_SELECTED_IS_HD(channel)) {
+ BX_ERROR(("read multiple issued to non-disk"));
+ command_aborted(channel, value);
+ break;
+ }
+
+ BX_SELECTED_CONTROLLER(channel).current_command = value;
+
+ // Lose98 accesses 0/0/0 in CHS mode
+ if (!BX_SELECTED_CONTROLLER(channel).lba_mode &&
+ !BX_SELECTED_CONTROLLER(channel).head_no &&
+ !BX_SELECTED_CONTROLLER(channel).cylinder_no &&
+ !BX_SELECTED_CONTROLLER(channel).sector_no) {
+ BX_INFO(("Read from 0/0/0, aborting command"));
+ command_aborted(channel, value);
+ break;
+ }
+
+#if TEST_READ_BEYOND_END==2
+ BX_SELECTED_CONTROLLER(channel).cylinder_no += 100000;
+#endif
+ if (!calculate_logical_address(channel, &logical_sector)) {
+ BX_ERROR(("initial read from sector %lu out of bounds, aborting", (unsigned long)logical_sector));
+ command_aborted(channel, value);
+ break;
+ }
+#if TEST_READ_BEYOND_END==3
+ logical_sector += 100000;
+#endif
+ ret=BX_SELECTED_DRIVE(channel).hard_drive->lseek(logical_sector * 512, SEEK_SET);
+ if (ret < 0) {
+ BX_ERROR (("could not lseek() hard drive image file, aborting"));
+ command_aborted(channel, value);
+ break;
+ }
+ ret = BX_SELECTED_DRIVE(channel).hard_drive->read((bx_ptr_t) BX_SELECTED_CONTROLLER(channel).buffer, 512);
+ if (ret < 512) {
+ BX_ERROR(("logical sector was %lu", (unsigned long)logical_sector));
+ BX_ERROR(("could not read() hard drive image file at byte %lu", (unsigned long)logical_sector*512));
+ command_aborted(channel, value);
+ break;
+ }
+
+ BX_SELECTED_CONTROLLER(channel).error_register = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ raise_interrupt(channel);
+ break;
+
+ case 0x30: /* WRITE SECTORS, with retries */
+ /* update sector_no, always points to current sector
+ * after each sector is read to buffer, DRQ bit set and issue IRQ
+ * if interrupt handler transfers all data words into main memory,
+ * and more sectors to read, then set BSY bit again, clear DRQ and
+ * read next sector into buffer
+ * sector count of 0 means 256 sectors
+ */
+
+ if (!BX_SELECTED_IS_HD(channel))
+ BX_PANIC(("write multiple issued to non-disk"));
+
+ if (BX_SELECTED_CONTROLLER(channel).status.busy) {
+ BX_PANIC(("write command: BSY bit set"));
+ }
+ BX_SELECTED_CONTROLLER(channel).current_command = value;
+
+ // implicit seek done :^)
+ BX_SELECTED_CONTROLLER(channel).error_register = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ // BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ break;
+
+ case 0x90: // EXECUTE DEVICE DIAGNOSTIC
+ if (BX_SELECTED_CONTROLLER(channel).status.busy) {
+ BX_PANIC(("diagnostic command: BSY bit set"));
+ }
+ if (!BX_SELECTED_IS_HD(channel))
+ BX_PANIC(("drive diagnostics issued to non-disk"));
+ BX_SELECTED_CONTROLLER(channel).error_register = 0x81; // Drive 1 failed, no error on drive 0
+ // BX_SELECTED_CONTROLLER(channel).status.busy = 0; // not needed
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ break;
+
+ case 0x91: // INITIALIZE DRIVE PARAMETERS
+ if (BX_SELECTED_CONTROLLER(channel).status.busy) {
+ BX_PANIC(("init drive parameters command: BSY bit set"));
+ }
+ if (!BX_SELECTED_IS_HD(channel))
+ BX_PANIC(("initialize drive parameters issued to non-disk"));
+ // sets logical geometry of specified drive
+ BX_DEBUG(("init drive params: sec=%u, drive sel=%u, head=%u",
+ (unsigned) BX_SELECTED_CONTROLLER(channel).sector_count,
+ (unsigned) BX_HD_THIS channels[channel].drive_select,
+ (unsigned) BX_SELECTED_CONTROLLER(channel).head_no));
+ if (!BX_SELECTED_IS_PRESENT(channel)) {
+ BX_PANIC(("init drive params: disk ata%d-%d not present", channel, BX_SLAVE_SELECTED(channel)));
+ //BX_SELECTED_CONTROLLER(channel).error_register = 0x12;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ raise_interrupt(channel);
+ break;
+ }
+ if (BX_SELECTED_CONTROLLER(channel).sector_count != BX_SELECTED_DRIVE(channel).hard_drive->sectors)
+ BX_PANIC(("init drive params: sector count doesnt match %d!=%d", BX_SELECTED_CONTROLLER(channel).sector_count, BX_SELECTED_DRIVE(channel).hard_drive->sectors));
+ if ( BX_SELECTED_CONTROLLER(channel).head_no != (BX_SELECTED_DRIVE(channel).hard_drive->heads-1) )
+ BX_PANIC(("init drive params: head number doesn't match %d != %d",BX_SELECTED_CONTROLLER(channel).head_no, BX_SELECTED_DRIVE(channel).hard_drive->heads-1));
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ raise_interrupt(channel);
+ break;
+
+ case 0xec: // IDENTIFY DEVICE
+ if (bx_options.OnewHardDriveSupport->get ()) {
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("Drive ID Command issued : 0xec "));
+
+ if (!BX_SELECTED_IS_PRESENT(channel)) {
+ BX_INFO(("disk ata%d-%d not present, aborting",channel,BX_SLAVE_SELECTED(channel)));
+ command_aborted(channel, value);
+ break;
+ }
+ if (BX_SELECTED_IS_CD(channel)) {
+ BX_SELECTED_CONTROLLER(channel).head_no = 0;
+ BX_SELECTED_CONTROLLER(channel).sector_count = 1;
+ BX_SELECTED_CONTROLLER(channel).sector_no = 1;
+ BX_SELECTED_CONTROLLER(channel).cylinder_no = 0xeb14;
+ command_aborted(channel, 0xec);
+ } else {
+ BX_SELECTED_CONTROLLER(channel).current_command = value;
+ BX_SELECTED_CONTROLLER(channel).error_register = 0;
+
+ // See ATA/ATAPI-4, 8.12
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ raise_interrupt(channel);
+ identify_drive(channel);
+ }
+ }
+ else {
+ BX_INFO(("sent IDENTIFY DEVICE (0xec) to old hard drive"));
+ command_aborted(channel, value);
+ }
+ break;
+
+ case 0xef: // SET FEATURES
+ switch(BX_SELECTED_CONTROLLER(channel).features) {
+ case 0x02: // Enable and
+ case 0x82: // Disable write cache.
+ case 0xAA: // Enable and
+ case 0x55: // Disable look-ahead cache.
+ case 0xCC: // Enable and
+ case 0x66: // Disable reverting to power-on default
+ BX_INFO(("SET FEATURES subcommand 0x%02x not supported by disk.", (unsigned) BX_SELECTED_CONTROLLER(channel).features));
+ command_aborted(channel, value);
+ break;
+
+ default:
+ BX_PANIC(("SET FEATURES with unknown subcommand: 0x%02x", (unsigned) BX_SELECTED_CONTROLLER(channel).features ));
+ // We'd better signal the error if the user chose to continue
+ command_aborted(channel, value);
+ }
+ break;
+
+ case 0x40: // READ VERIFY SECTORS
+ if (bx_options.OnewHardDriveSupport->get ()) {
+ if (!BX_SELECTED_IS_HD(channel))
+ BX_PANIC(("read verify issued to non-disk"));
+ BX_INFO(("Verify Command : 0x40 ! "));
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ raise_interrupt(channel);
+ }
+ else {
+ BX_INFO(("sent READ VERIFY SECTORS (0x40) to old hard drive"));
+ command_aborted(channel, value);
+ }
+ break;
+
+ case 0xc6: // SET MULTIPLE MODE (mch)
+ if (BX_SELECTED_CONTROLLER(channel).sector_count != 128 &&
+ BX_SELECTED_CONTROLLER(channel).sector_count != 64 &&
+ BX_SELECTED_CONTROLLER(channel).sector_count != 32 &&
+ BX_SELECTED_CONTROLLER(channel).sector_count != 16 &&
+ BX_SELECTED_CONTROLLER(channel).sector_count != 8 &&
+ BX_SELECTED_CONTROLLER(channel).sector_count != 4 &&
+ BX_SELECTED_CONTROLLER(channel).sector_count != 2)
+ command_aborted(channel, value);
+
+ if (!BX_SELECTED_IS_HD(channel))
+ BX_PANIC(("set multiple mode issued to non-disk"));
+
+ BX_SELECTED_CONTROLLER(channel).sectors_per_block = BX_SELECTED_CONTROLLER(channel).sector_count;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ break;
+
+ // ATAPI commands
+ case 0xa1: // IDENTIFY PACKET DEVICE
+ if (BX_SELECTED_IS_CD(channel)) {
+ BX_SELECTED_CONTROLLER(channel).current_command = value;
+ BX_SELECTED_CONTROLLER(channel).error_register = 0;
+
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ raise_interrupt(channel);
+ identify_ATAPI_drive(channel);
+ } else {
+ command_aborted(channel, 0xa1);
+ }
+ break;
+
+ case 0x08: // DEVICE RESET (atapi)
+ if (BX_SELECTED_IS_CD(channel)) {
+ BX_SELECTED_CONTROLLER(channel).status.busy = 1;
+ BX_SELECTED_CONTROLLER(channel).error_register &= ~(1 << 7);
+
+ // device signature
+ BX_SELECTED_CONTROLLER(channel).head_no = 0;
+ BX_SELECTED_CONTROLLER(channel).sector_count = 1;
+ BX_SELECTED_CONTROLLER(channel).sector_no = 1;
+ BX_SELECTED_CONTROLLER(channel).cylinder_no = 0xeb14;
+
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+
+ } else {
+ BX_DEBUG(("ATAPI Device Reset on non-cd device"));
+ command_aborted(channel, 0x08);
+ }
+ break;
+
+ case 0xa0: // SEND PACKET (atapi)
+ if (BX_SELECTED_IS_CD(channel)) {
+ // PACKET
+ if (BX_SELECTED_CONTROLLER(channel).features & (1 << 0))
+ BX_PANIC(("PACKET-DMA not supported"));
+ if (BX_SELECTED_CONTROLLER(channel).features & (1 << 1))
+ BX_PANIC(("PACKET-overlapped not supported"));
+
+ // We're already ready!
+ BX_SELECTED_CONTROLLER(channel).sector_count = 1;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ // serv bit??
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ // NOTE: no interrupt here
+ BX_SELECTED_CONTROLLER(channel).current_command = value;
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ } else {
+ command_aborted (channel, 0xa0);
+ }
+ break;
+
+ case 0xa2: // SERVICE (atapi), optional
+ if (BX_SELECTED_IS_CD(channel)) {
+ BX_PANIC(("ATAPI SERVICE not implemented"));
+ } else {
+ command_aborted (channel, 0xa2);
+ }
+ break;
+
+ // power management
+ case 0xe5: // CHECK POWER MODE
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ BX_SELECTED_CONTROLLER(channel).sector_count = 0xff; // Active or Idle mode
+ raise_interrupt(channel);
+ break;
+
+ case 0x70: // SEEK (cgs)
+ if (BX_SELECTED_IS_HD(channel)) {
+ BX_DEBUG(("write cmd 0x70 (SEEK) executing"));
+ if (!calculate_logical_address(channel, &logical_sector)) {
+ BX_ERROR(("initial seek to sector %lu out of bounds, aborting", (unsigned long)logical_sector));
+ command_aborted(channel, value);
+ break;
+ }
+ BX_SELECTED_CONTROLLER(channel).error_register = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ BX_DEBUG(("s[0].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[0]).controller.control.disable_irq));
+ BX_DEBUG(("s[1].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[1]).controller.control.disable_irq));
+ BX_DEBUG(("SEEK completed. error_register = %02x", BX_SELECTED_CONTROLLER(channel).error_register));
+ raise_interrupt(channel);
+ BX_DEBUG(("SEEK interrupt completed"));
+ } else {
+ BX_ERROR(("write cmd 0x70 (SEEK) not supported for non-disk"));
+ command_aborted(channel, 0x70);
+ }
+ break;
+
+
+
+ // List all the write operations that are defined in the ATA/ATAPI spec
+ // that we don't support. Commands that are listed here will cause a
+ // BX_ERROR, which is non-fatal, and the command will be aborted.
+ case 0x22: BX_ERROR(("write cmd 0x22 (READ LONG) not supported")); command_aborted(channel, 0x22); break;
+ case 0x23: BX_ERROR(("write cmd 0x23 (READ LONG NO RETRY) not supported")); command_aborted(channel, 0x23); break;
+ case 0x24: BX_ERROR(("write cmd 0x24 (READ SECTORS EXT) not supported"));command_aborted(channel, 0x24); break;
+ case 0x25: BX_ERROR(("write cmd 0x25 (READ DMA EXT) not supported"));command_aborted(channel, 0x25); break;
+ case 0x26: BX_ERROR(("write cmd 0x26 (READ DMA QUEUED EXT) not supported"));command_aborted(channel, 0x26); break;
+ case 0x27: BX_ERROR(("write cmd 0x27 (READ NATIVE MAX ADDRESS EXT) not supported"));command_aborted(channel, 0x27); break;
+ case 0x29: BX_ERROR(("write cmd 0x29 (READ MULTIPLE EXT) not supported"));command_aborted(channel, 0x29); break;
+ case 0x2A: BX_ERROR(("write cmd 0x2A (READ STREAM DMA) not supported"));command_aborted(channel, 0x2A); break;
+ case 0x2B: BX_ERROR(("write cmd 0x2B (READ STREAM PIO) not supported"));command_aborted(channel, 0x2B); break;
+ case 0x2F: BX_ERROR(("write cmd 0x2F (READ LOG EXT) not supported"));command_aborted(channel, 0x2F); break;
+ case 0x31: BX_ERROR(("write cmd 0x31 (WRITE SECTORS NO RETRY) not supported")); command_aborted(channel, 0x31); break;
+ case 0x32: BX_ERROR(("write cmd 0x32 (WRITE LONG) not supported")); command_aborted(channel, 0x32); break;
+ case 0x33: BX_ERROR(("write cmd 0x33 (WRITE LONG NO RETRY) not supported")); command_aborted(channel, 0x33); break;
+ case 0x34: BX_ERROR(("write cmd 0x34 (WRITE SECTORS EXT) not supported"));command_aborted(channel, 0x34); break;
+ case 0x35: BX_ERROR(("write cmd 0x35 (WRITE DMA EXT) not supported"));command_aborted(channel, 0x35); break;
+ case 0x36: BX_ERROR(("write cmd 0x36 (WRITE DMA QUEUED EXT) not supported"));command_aborted(channel, 0x36); break;
+ case 0x37: BX_ERROR(("write cmd 0x37 (SET MAX ADDRESS EXT) not supported"));command_aborted(channel, 0x37); break;
+ case 0x38: BX_ERROR(("write cmd 0x38 (CFA WRITE SECTORS W/OUT ERASE) not supported"));command_aborted(channel, 0x38); break;
+ case 0x39: BX_ERROR(("write cmd 0x39 (WRITE MULTIPLE EXT) not supported"));command_aborted(channel, 0x39); break;
+ case 0x3A: BX_ERROR(("write cmd 0x3A (WRITE STREAM DMA) not supported"));command_aborted(channel, 0x3A); break;
+ case 0x3B: BX_ERROR(("write cmd 0x3B (WRITE STREAM PIO) not supported"));command_aborted(channel, 0x3B); break;
+ case 0x3F: BX_ERROR(("write cmd 0x3F (WRITE LOG EXT) not supported"));command_aborted(channel, 0x3F); break;
+ case 0x41: BX_ERROR(("write cmd 0x41 (READ VERIFY SECTORS NO RETRY) not supported")); command_aborted(channel, 0x41); break;
+ case 0x42: BX_ERROR(("write cmd 0x42 (READ VERIFY SECTORS EXT) not supported"));command_aborted(channel, 0x42); break;
+ case 0x50: BX_ERROR(("write cmd 0x50 (FORMAT TRACK) not supported")); command_aborted(channel, 0x50); break;
+ case 0x51: BX_ERROR(("write cmd 0x51 (CONFIGURE STREAM) not supported"));command_aborted(channel, 0x51); break;
+ case 0x87: BX_ERROR(("write cmd 0x87 (CFA TRANSLATE SECTOR) not supported"));command_aborted(channel, 0x87); break;
+ case 0x92: BX_ERROR(("write cmd 0x92 (DOWNLOAD MICROCODE) not supported"));command_aborted(channel, 0x92); break;
+ case 0x94: BX_ERROR(("write cmd 0x94 (STANDBY IMMEDIATE) not supported")); command_aborted(channel, 0x94); break;
+ case 0x95: BX_ERROR(("write cmd 0x95 (IDLE IMMEDIATE) not supported")); command_aborted(channel, 0x95); break;
+ case 0x96: BX_ERROR(("write cmd 0x96 (STANDBY) not supported")); command_aborted(channel, 0x96); break;
+ case 0x97: BX_ERROR(("write cmd 0x97 (IDLE) not supported")); command_aborted(channel, 0x97); break;
+ case 0x98: BX_ERROR(("write cmd 0x98 (CHECK POWER MODE) not supported")); command_aborted(channel, 0x98); break;
+ case 0x99: BX_ERROR(("write cmd 0x99 (SLEEP) not supported")); command_aborted(channel, 0x99); break;
+ case 0xB0: BX_ERROR(("write cmd 0xB0 (SMART commands) not supported"));command_aborted(channel, 0xB0); break;
+ case 0xB1: BX_ERROR(("write cmd 0xB1 (DEVICE CONFIGURATION commands) not supported"));command_aborted(channel, 0xB1); break;
+ case 0xC0: BX_ERROR(("write cmd 0xC0 (CFA ERASE SECTORS) not supported"));command_aborted(channel, 0xC0); break;
+ case 0xC4: BX_ERROR(("write cmd 0xC4 (READ MULTIPLE) not supported"));command_aborted(channel, 0xC4); break;
+ case 0xC5: BX_ERROR(("write cmd 0xC5 (WRITE MULTIPLE) not supported"));command_aborted(channel, 0xC5); break;
+ case 0xC7: BX_ERROR(("write cmd 0xC7 (READ DMA QUEUED) not supported"));command_aborted(channel, 0xC7); break;
+ case 0xC8: BX_ERROR(("write cmd 0xC8 (READ DMA) not supported"));command_aborted(channel, 0xC8); break;
+ case 0xC9: BX_ERROR(("write cmd 0xC9 (READ DMA NO RETRY) not supported")); command_aborted(channel, 0xC9); break;
+ case 0xCA: BX_ERROR(("write cmd 0xCA (WRITE DMA) not supported"));command_aborted(channel, 0xCA); break;
+ case 0xCC: BX_ERROR(("write cmd 0xCC (WRITE DMA QUEUED) not supported"));command_aborted(channel, 0xCC); break;
+ case 0xCD: BX_ERROR(("write cmd 0xCD (CFA WRITE MULTIPLE W/OUT ERASE) not supported"));command_aborted(channel, 0xCD); break;
+ case 0xD1: BX_ERROR(("write cmd 0xD1 (CHECK MEDIA CARD TYPE) not supported"));command_aborted(channel, 0xD1); break;
+ case 0xDA: BX_ERROR(("write cmd 0xDA (GET MEDIA STATUS) not supported"));command_aborted(channel, 0xDA); break;
+ case 0xDE: BX_ERROR(("write cmd 0xDE (MEDIA LOCK) not supported"));command_aborted(channel, 0xDE); break;
+ case 0xDF: BX_ERROR(("write cmd 0xDF (MEDIA UNLOCK) not supported"));command_aborted(channel, 0xDF); break;
+ case 0xE0: BX_ERROR(("write cmd 0xE0 (STANDBY IMMEDIATE) not supported"));command_aborted(channel, 0xE0); break;
+ case 0xE1: BX_ERROR(("write cmd 0xE1 (IDLE IMMEDIATE) not supported"));command_aborted(channel, 0xE1); break;
+ case 0xE2: BX_ERROR(("write cmd 0xE2 (STANDBY) not supported"));command_aborted(channel, 0xE2); break;
+ case 0xE3: BX_ERROR(("write cmd 0xE3 (IDLE) not supported"));command_aborted(channel, 0xE3); break;
+ case 0xE4: BX_ERROR(("write cmd 0xE4 (READ BUFFER) not supported"));command_aborted(channel, 0xE4); break;
+ case 0xE6: BX_ERROR(("write cmd 0xE6 (SLEEP) not supported"));command_aborted(channel, 0xE6); break;
+ case 0xE7: BX_ERROR(("write cmd 0xE7 (FLUSH CACHE) not supported"));command_aborted(channel, 0xE7); break;
+ case 0xE8: BX_ERROR(("write cmd 0xE8 (WRITE BUFFER) not supported"));command_aborted(channel, 0xE8); break;
+ case 0xEA: BX_ERROR(("write cmd 0xEA (FLUSH CACHE EXT) not supported"));command_aborted(channel, 0xEA); break;
+ case 0xED: BX_ERROR(("write cmd 0xED (MEDIA EJECT) not supported"));command_aborted(channel, 0xED); break;
+ case 0xF1: BX_ERROR(("write cmd 0xF1 (SECURITY SET PASSWORD) not supported"));command_aborted(channel, 0xF1); break;
+ case 0xF2: BX_ERROR(("write cmd 0xF2 (SECURITY UNLOCK) not supported"));command_aborted(channel, 0xF2); break;
+ case 0xF3: BX_ERROR(("write cmd 0xF3 (SECURITY ERASE PREPARE) not supported"));command_aborted(channel, 0xF3); break;
+ case 0xF4: BX_ERROR(("write cmd 0xF4 (SECURITY ERASE UNIT) not supported"));command_aborted(channel, 0xF4); break;
+ case 0xF5: BX_ERROR(("write cmd 0xF5 (SECURITY FREEZE LOCK) not supported"));command_aborted(channel, 0xF5); break;
+ case 0xF6: BX_ERROR(("write cmd 0xF6 (SECURITY DISABLE PASSWORD) not supported"));command_aborted(channel, 0xF6); break;
+ case 0xF8: BX_ERROR(("write cmd 0xF8 (READ NATIVE MAX ADDRESS) not supported"));command_aborted(channel, 0xF8); break;
+ case 0xF9: BX_ERROR(("write cmd 0xF9 (SET MAX ADDRESS) not supported"));command_aborted(channel, 0xF9); break;
+
+ default:
+ BX_PANIC(("IO write(0x%04x): command 0x%02x", address, (unsigned) value));
+ // if user foolishly decides to continue, abort the command
+ // so that the software knows the drive didn't understand it.
+ command_aborted(channel, value);
+ }
+ break;
+
+ case 0x16: // hard disk adapter control 0x3f6
+ // (mch) Even if device 1 was selected, a write to this register
+ // goes to device 0 (if device 1 is absent)
+
+ prev_control_reset = BX_SELECTED_CONTROLLER(channel).control.reset;
+ BX_HD_THIS channels[channel].drives[0].controller.control.reset = value & 0x04;
+ BX_HD_THIS channels[channel].drives[1].controller.control.reset = value & 0x04;
+ // CGS: was: BX_SELECTED_CONTROLLER(channel).control.disable_irq = value & 0x02;
+ BX_HD_THIS channels[channel].drives[0].controller.control.disable_irq = value & 0x02;
+ BX_HD_THIS channels[channel].drives[1].controller.control.disable_irq = value & 0x02;
+
+ BX_DEBUG(( "adpater control reg: reset controller = %d",
+ (unsigned) (BX_SELECTED_CONTROLLER(channel).control.reset) ? 1 : 0 ));
+ BX_DEBUG(( "adpater control reg: disable_irq(X) = %d",
+ (unsigned) (BX_SELECTED_CONTROLLER(channel).control.disable_irq) ? 1 : 0 ));
+
+ if (!prev_control_reset && BX_SELECTED_CONTROLLER(channel).control.reset) {
+ // transition from 0 to 1 causes all drives to reset
+ BX_DEBUG(("hard drive: RESET"));
+
+ // (mch) Set BSY, drive not ready
+ for (int id = 0; id < 2; id++) {
+ BX_CONTROLLER(channel,id).status.busy = 1;
+ BX_CONTROLLER(channel,id).status.drive_ready = 0;
+ BX_CONTROLLER(channel,id).reset_in_progress = 1;
+
+ BX_CONTROLLER(channel,id).status.write_fault = 0;
+ BX_CONTROLLER(channel,id).status.seek_complete = 1;
+ BX_CONTROLLER(channel,id).status.drq = 0;
+ BX_CONTROLLER(channel,id).status.corrected_data = 0;
+ BX_CONTROLLER(channel,id).status.err = 0;
+
+ BX_CONTROLLER(channel,id).error_register = 0x01; // diagnostic code: no error
+
+ BX_CONTROLLER(channel,id).current_command = 0x00;
+ BX_CONTROLLER(channel,id).buffer_index = 0;
+
+ BX_CONTROLLER(channel,id).sectors_per_block = 0x80;
+ BX_CONTROLLER(channel,id).lba_mode = 0;
+
+ BX_CONTROLLER(channel,id).control.disable_irq = 0;
+ DEV_pic_lower_irq(BX_HD_THIS channels[channel].irq);
+ }
+ } else if (BX_SELECTED_CONTROLLER(channel).reset_in_progress &&
+ !BX_SELECTED_CONTROLLER(channel).control.reset) {
+ // Clear BSY and DRDY
+ BX_DEBUG(("Reset complete {%s}", BX_SELECTED_TYPE_STRING(channel)));
+ for (int id = 0; id < 2; id++) {
+ BX_CONTROLLER(channel,id).status.busy = 0;
+ BX_CONTROLLER(channel,id).status.drive_ready = 1;
+ BX_CONTROLLER(channel,id).reset_in_progress = 0;
+
+ // Device signature
+ if (BX_DRIVE_IS_HD(channel,id)) {
+ BX_CONTROLLER(channel,id).head_no = 0;
+ BX_CONTROLLER(channel,id).sector_count = 1;
+ BX_CONTROLLER(channel,id).sector_no = 1;
+ BX_CONTROLLER(channel,id).cylinder_no = 0;
+ } else {
+ BX_CONTROLLER(channel,id).head_no = 0;
+ BX_CONTROLLER(channel,id).sector_count = 1;
+ BX_CONTROLLER(channel,id).sector_no = 1;
+ BX_CONTROLLER(channel,id).cylinder_no = 0xeb14;
+ }
+ }
+ }
+ BX_DEBUG(("s[0].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[0]).controller.control.disable_irq));
+ BX_DEBUG(("s[1].controller.control.disable_irq = %02x", (BX_HD_THIS channels[channel].drives[1]).controller.control.disable_irq));
+ break;
+
+ default:
+ BX_PANIC(("hard drive: io write to address %x = %02x",
+ (unsigned) address, (unsigned) value));
+ }
+}
+
+ void
+bx_hard_drive_c::close_harddrive(void)
+{
+ for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if(BX_HD_THIS channels[channel].drives[0].hard_drive != NULL)
+ BX_HD_THIS channels[channel].drives[0].hard_drive->close();
+ if(BX_HD_THIS channels[channel].drives[1].hard_drive != NULL)
+ BX_HD_THIS channels[channel].drives[1].hard_drive->close();
+ }
+}
+
+
+ bx_bool BX_CPP_AttrRegparmN(2)
+bx_hard_drive_c::calculate_logical_address(Bit8u channel, off_t *sector)
+{
+ off_t logical_sector;
+
+ if (BX_SELECTED_CONTROLLER(channel).lba_mode) {
+ //bx_printf ("disk: calculate: %d %d %d\n", ((Bit32u)BX_SELECTED_CONTROLLER(channel).head_no), ((Bit32u)BX_SELECTED_CONTROLLER(channel).cylinder_no), (Bit32u)BX_SELECTED_CONTROLLER(channel).sector_no);
+ logical_sector = ((Bit32u)BX_SELECTED_CONTROLLER(channel).head_no) << 24 |
+ ((Bit32u)BX_SELECTED_CONTROLLER(channel).cylinder_no) << 8 |
+ (Bit32u)BX_SELECTED_CONTROLLER(channel).sector_no;
+ //bx_printf ("disk: result: %u\n", logical_sector);
+ } else
+ logical_sector = (BX_SELECTED_CONTROLLER(channel).cylinder_no * BX_SELECTED_DRIVE(channel).hard_drive->heads *
+ BX_SELECTED_DRIVE(channel).hard_drive->sectors) +
+ (BX_SELECTED_CONTROLLER(channel).head_no * BX_SELECTED_DRIVE(channel).hard_drive->sectors) +
+ (BX_SELECTED_CONTROLLER(channel).sector_no - 1);
+
+ Bit32u sector_count=
+ (Bit32u)BX_SELECTED_DRIVE(channel).hard_drive->cylinders *
+ (Bit32u)BX_SELECTED_DRIVE(channel).hard_drive->heads *
+ (Bit32u)BX_SELECTED_DRIVE(channel).hard_drive->sectors;
+
+ if (logical_sector >= sector_count) {
+ BX_ERROR (("calc_log_addr: out of bounds (%d/%d)", (Bit32u)logical_sector, sector_count));
+ return false;
+ }
+ *sector = logical_sector;
+ return true;
+}
+
+ void BX_CPP_AttrRegparmN(1)
+bx_hard_drive_c::increment_address(Bit8u channel)
+{
+ BX_SELECTED_CONTROLLER(channel).sector_count--;
+
+ if (BX_SELECTED_CONTROLLER(channel).lba_mode) {
+ off_t current_address;
+ calculate_logical_address(channel, &current_address);
+ current_address++;
+ BX_SELECTED_CONTROLLER(channel).head_no = (Bit8u)((current_address >> 24) & 0xf);
+ BX_SELECTED_CONTROLLER(channel).cylinder_no = (Bit16u)((current_address >> 8) & 0xffff);
+ BX_SELECTED_CONTROLLER(channel).sector_no = (Bit8u)((current_address) & 0xff);
+ } else {
+ BX_SELECTED_CONTROLLER(channel).sector_no++;
+ if (BX_SELECTED_CONTROLLER(channel).sector_no > BX_SELECTED_DRIVE(channel).hard_drive->sectors) {
+ BX_SELECTED_CONTROLLER(channel).sector_no = 1;
+ BX_SELECTED_CONTROLLER(channel).head_no++;
+ if (BX_SELECTED_CONTROLLER(channel).head_no >= BX_SELECTED_DRIVE(channel).hard_drive->heads) {
+ BX_SELECTED_CONTROLLER(channel).head_no = 0;
+ BX_SELECTED_CONTROLLER(channel).cylinder_no++;
+ if (BX_SELECTED_CONTROLLER(channel).cylinder_no >= BX_SELECTED_DRIVE(channel).hard_drive->cylinders)
+ BX_SELECTED_CONTROLLER(channel).cylinder_no = BX_SELECTED_DRIVE(channel).hard_drive->cylinders - 1;
+ }
+ }
+ }
+}
+
+ void
+bx_hard_drive_c::identify_ATAPI_drive(Bit8u channel)
+{
+ unsigned i;
+
+ BX_SELECTED_DRIVE(channel).id_drive[0] = (2 << 14) | (5 << 8) | (1 << 7) | (2 << 5) | (0 << 0); // Removable CDROM, 50us response, 12 byte packets
+
+ for (i = 1; i <= 9; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ const char* serial_number = " VT00001\0\0\0\0\0\0\0\0\0\0\0\0";
+ for (i = 0; i < 10; i++) {
+ BX_SELECTED_DRIVE(channel).id_drive[10+i] = (serial_number[i*2] << 8) |
+ serial_number[i*2 + 1];
+ }
+
+ for (i = 20; i <= 22; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ const char* firmware = "ALPHA1 ";
+ for (i = 0; i < strlen(firmware)/2; i++) {
+ BX_SELECTED_DRIVE(channel).id_drive[23+i] = (firmware[i*2] << 8) |
+ firmware[i*2 + 1];
+ }
+ BX_ASSERT((23+i) == 27);
+
+ for (i = 0; i < strlen((char *) BX_SELECTED_MODEL(channel))/2; i++) {
+ BX_SELECTED_DRIVE(channel).id_drive[27+i] = (BX_SELECTED_MODEL(channel)[i*2] << 8) |
+ BX_SELECTED_MODEL(channel)[i*2 + 1];
+ }
+ BX_ASSERT((27+i) == 47);
+
+ BX_SELECTED_DRIVE(channel).id_drive[47] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[48] = 1; // 32 bits access
+
+ BX_SELECTED_DRIVE(channel).id_drive[49] = (1 << 9); // LBA supported
+
+ BX_SELECTED_DRIVE(channel).id_drive[50] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[51] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[52] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[53] = 3; // words 64-70, 54-58 valid
+
+ for (i = 54; i <= 62; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // copied from CFA540A
+ BX_SELECTED_DRIVE(channel).id_drive[63] = 0x0103; // variable (DMA stuff)
+ BX_SELECTED_DRIVE(channel).id_drive[64] = 0x0001; // PIO
+ BX_SELECTED_DRIVE(channel).id_drive[65] = 0x00b4;
+ BX_SELECTED_DRIVE(channel).id_drive[66] = 0x00b4;
+ BX_SELECTED_DRIVE(channel).id_drive[67] = 0x012c;
+ BX_SELECTED_DRIVE(channel).id_drive[68] = 0x00b4;
+
+ BX_SELECTED_DRIVE(channel).id_drive[69] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[70] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[71] = 30; // faked
+ BX_SELECTED_DRIVE(channel).id_drive[72] = 30; // faked
+ BX_SELECTED_DRIVE(channel).id_drive[73] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[74] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[75] = 0;
+
+ for (i = 76; i <= 79; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[80] = 0x1e; // supports up to ATA/ATAPI-4
+ BX_SELECTED_DRIVE(channel).id_drive[81] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[82] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[83] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[84] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[85] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[86] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[87] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[88] = 0;
+
+ for (i = 89; i <= 126; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[127] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[128] = 0;
+
+ for (i = 129; i <= 159; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ for (i = 160; i <= 255; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // now convert the id_drive array (native 256 word format) to
+ // the controller buffer (512 bytes)
+ Bit16u temp16;
+ for (i = 0; i <= 255; i++) {
+ temp16 = BX_SELECTED_DRIVE(channel).id_drive[i];
+ BX_SELECTED_CONTROLLER(channel).buffer[i*2] = temp16 & 0x00ff;
+ BX_SELECTED_CONTROLLER(channel).buffer[i*2+1] = temp16 >> 8;
+ }
+}
+
+ void
+bx_hard_drive_c::identify_drive(Bit8u channel)
+{
+ unsigned i;
+ Bit32u temp32;
+ Bit16u temp16;
+
+#if defined(CONNER_CFA540A)
+ BX_SELECTED_DRIVE(channel).id_drive[0] = 0x0c5a;
+ BX_SELECTED_DRIVE(channel).id_drive[1] = 0x0418;
+ BX_SELECTED_DRIVE(channel).id_drive[2] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[3] = BX_SELECTED_DRIVE(channel).hard_drive->heads;
+ BX_SELECTED_DRIVE(channel).id_drive[4] = 0x9fb7;
+ BX_SELECTED_DRIVE(channel).id_drive[5] = 0x0289;
+ BX_SELECTED_DRIVE(channel).id_drive[6] = BX_SELECTED_DRIVE(channel).hard_drive->sectors;
+ BX_SELECTED_DRIVE(channel).id_drive[7] = 0x0030;
+ BX_SELECTED_DRIVE(channel).id_drive[8] = 0x000a;
+ BX_SELECTED_DRIVE(channel).id_drive[9] = 0x0000;
+
+ char* serial_number = " CA00GSQ\0\0\0\0\0\0\0\0\0\0\0\0";
+ for (i = 0; i < 10; i++) {
+ BX_SELECTED_DRIVE(channel).id_drive[10+i] = (serial_number[i*2] << 8) |
+ serial_number[i*2 + 1];
+ }
+
+ BX_SELECTED_DRIVE(channel).id_drive[20] = 3;
+ BX_SELECTED_DRIVE(channel).id_drive[21] = 512; // 512 Sectors = 256kB cache
+ BX_SELECTED_DRIVE(channel).id_drive[22] = 4;
+
+ char* firmware = "8FT054 ";
+ for (i = 0; i < strlen(firmware)/2; i++) {
+ BX_SELECTED_DRIVE(channel).id_drive[23+i] = (firmware[i*2] << 8) |
+ firmware[i*2 + 1];
+ }
+ BX_ASSERT((23+i) == 27);
+
+ char* model = "Conner Peripherals 540MB - CFA540A ";
+ for (i = 0; i < strlen(model)/2; i++) {
+ BX_SELECTED_DRIVE(channel).id_drive[27+i] = (model[i*2] << 8) |
+ model[i*2 + 1];
+ }
+ BX_ASSERT((27+i) == 47);
+
+ BX_SELECTED_DRIVE(channel).id_drive[47] = 0x8080; // multiple mode identification
+ BX_SELECTED_DRIVE(channel).id_drive[48] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[49] = 0x0f01;
+
+ BX_SELECTED_DRIVE(channel).id_drive[50] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[51] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[52] = 0x0002;
+ BX_SELECTED_DRIVE(channel).id_drive[53] = 0x0003;
+ BX_SELECTED_DRIVE(channel).id_drive[54] = 0x0418;
+
+ BX_SELECTED_DRIVE(channel).id_drive[55] = BX_SELECTED_DRIVE(channel).hard_drive->heads;
+ BX_SELECTED_DRIVE(channel).id_drive[56] = BX_SELECTED_DRIVE(channel).hard_drive->sectors;
+
+ BX_SELECTED_DRIVE(channel).id_drive[57] = 0x1e80;
+ BX_SELECTED_DRIVE(channel).id_drive[58] = 0x0010;
+ BX_SELECTED_DRIVE(channel).id_drive[59] = 0x0100 | BX_SELECTED_CONTROLLER(channel).sectors_per_block;
+ BX_SELECTED_DRIVE(channel).id_drive[60] = 0x20e0;
+ BX_SELECTED_DRIVE(channel).id_drive[61] = 0x0010;
+
+ BX_SELECTED_DRIVE(channel).id_drive[62] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[63] = 0x0103; // variable (DMA stuff)
+ BX_SELECTED_DRIVE(channel).id_drive[64] = 0x0001; // PIO
+ BX_SELECTED_DRIVE(channel).id_drive[65] = 0x00b4;
+ BX_SELECTED_DRIVE(channel).id_drive[66] = 0x00b4;
+ BX_SELECTED_DRIVE(channel).id_drive[67] = 0x012c;
+ BX_SELECTED_DRIVE(channel).id_drive[68] = 0x00b4;
+
+ for (i = 69; i <= 79; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[80] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[81] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[82] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[83] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[84] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[85] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[86] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[87] = 0;
+
+ for (i = 88; i <= 127; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[128] = 0x0418;
+ BX_SELECTED_DRIVE(channel).id_drive[129] = 0x103f;
+ BX_SELECTED_DRIVE(channel).id_drive[130] = 0x0418;
+ BX_SELECTED_DRIVE(channel).id_drive[131] = 0x103f;
+ BX_SELECTED_DRIVE(channel).id_drive[132] = 0x0004;
+ BX_SELECTED_DRIVE(channel).id_drive[133] = 0xffff;
+ BX_SELECTED_DRIVE(channel).id_drive[134] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[135] = 0x5050;
+
+ for (i = 136; i <= 144; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ BX_SELECTED_DRIVE(channel).id_drive[145] = 0x302e;
+ BX_SELECTED_DRIVE(channel).id_drive[146] = 0x3245;
+ BX_SELECTED_DRIVE(channel).id_drive[147] = 0x2020;
+ BX_SELECTED_DRIVE(channel).id_drive[148] = 0x2020;
+
+ for (i = 149; i <= 255; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+#else
+
+ // Identify Drive command return values definition
+ //
+ // This code is rehashed from some that was donated.
+ // I'm using ANSI X3.221-1994, AT Attachment Interface for Disk Drives
+ // and X3T10 2008D Working Draft for ATA-3
+
+
+ // Word 0: general config bit-significant info
+ // Note: bits 1-5 and 8-14 are now "Vendor specific (obsolete)"
+ // bit 15: 0=ATA device
+ // 1=ATAPI device
+ // bit 14: 1=format speed tolerance gap required
+ // bit 13: 1=track offset option available
+ // bit 12: 1=data strobe offset option available
+ // bit 11: 1=rotational speed tolerance is > 0,5% (typo?)
+ // bit 10: 1=disk transfer rate > 10Mbs
+ // bit 9: 1=disk transfer rate > 5Mbs but <= 10Mbs
+ // bit 8: 1=disk transfer rate <= 5Mbs
+ // bit 7: 1=removable cartridge drive
+ // bit 6: 1=fixed drive
+ // bit 5: 1=spindle motor control option implemented
+ // bit 4: 1=head switch time > 15 usec
+ // bit 3: 1=not MFM encoded
+ // bit 2: 1=soft sectored
+ // bit 1: 1=hard sectored
+ // bit 0: 0=reserved
+ BX_SELECTED_DRIVE(channel).id_drive[0] = 0x0040;
+
+ // Word 1: number of user-addressable cylinders in
+ // default translation mode. If the value in words 60-61
+ // exceed 16,515,072, this word shall contain 16,383.
+ BX_SELECTED_DRIVE(channel).id_drive[1] = BX_SELECTED_DRIVE(channel).hard_drive->cylinders;
+
+ // Word 2: reserved
+ BX_SELECTED_DRIVE(channel).id_drive[2] = 0;
+
+ // Word 3: number of user-addressable heads in default
+ // translation mode
+ BX_SELECTED_DRIVE(channel).id_drive[3] = BX_SELECTED_DRIVE(channel).hard_drive->heads;
+
+ // Word 4: # unformatted bytes per translated track in default xlate mode
+ // Word 5: # unformatted bytes per sector in default xlated mode
+ // Word 6: # user-addressable sectors per track in default xlate mode
+ // Note: words 4,5 are now "Vendor specific (obsolete)"
+ BX_SELECTED_DRIVE(channel).id_drive[4] = (512 * BX_SELECTED_DRIVE(channel).hard_drive->sectors);
+ BX_SELECTED_DRIVE(channel).id_drive[5] = 512;
+ BX_SELECTED_DRIVE(channel).id_drive[6] = BX_SELECTED_DRIVE(channel).hard_drive->sectors;
+
+ // Word 7-9: Vendor specific
+ for (i=7; i<=9; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // Word 10-19: Serial number (20 ASCII characters, 0000h=not specified)
+ // This field is right justified and padded with spaces (20h).
+ for (i=10; i<=19; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // Word 20: buffer type
+ // 0000h = not specified
+ // 0001h = single ported single sector buffer which is
+ // not capable of simulataneous data xfers to/from
+ // the host and the disk.
+ // 0002h = dual ported multi-sector buffer capable of
+ // simulatenous data xfers to/from the host and disk.
+ // 0003h = dual ported mutli-sector buffer capable of
+ // simulatenous data xfers with a read caching
+ // capability.
+ // 0004h-ffffh = reserved
+ BX_SELECTED_DRIVE(channel).id_drive[20] = 3;
+
+ // Word 21: buffer size in 512 byte increments, 0000h = not specified
+ BX_SELECTED_DRIVE(channel).id_drive[21] = 512; // 512 Sectors = 256kB cache
+
+ // Word 22: # of ECC bytes available on read/write long cmds
+ // 0000h = not specified
+ BX_SELECTED_DRIVE(channel).id_drive[22] = 4;
+
+ // Word 23..26: Firmware revision (8 ascii chars, 0000h=not specified)
+ // This field is left justified and padded with spaces (20h)
+ for (i=23; i<=26; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // Word 27..46: Model number (40 ascii chars, 0000h=not specified)
+ // This field is left justified and padded with spaces (20h)
+// for (i=27; i<=46; i++)
+// BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+ for (i=0; i<20; i++) {
+ BX_SELECTED_DRIVE(channel).id_drive[27+i] = (BX_SELECTED_MODEL(channel)[i*2] << 8) |
+ BX_SELECTED_MODEL(channel)[i*2 + 1];
+ }
+
+ // Word 47: 15-8 Vendor unique
+ // 7-0 00h= read/write multiple commands not implemented
+ // xxh= maximum # of sectors that can be transferred
+ // per interrupt on read and write multiple commands
+ BX_SELECTED_DRIVE(channel).id_drive[47] = max_multiple_sectors;
+
+ // Word 48: 0000h = cannot perform dword IO
+ // 0001h = can perform dword IO
+ BX_SELECTED_DRIVE(channel).id_drive[48] = 1;
+
+ // Word 49: Capabilities
+ // 15-10: 0 = reserved
+ // 9: 1 = LBA supported
+ // 8: 1 = DMA supported
+ // 7-0: Vendor unique
+ BX_SELECTED_DRIVE(channel).id_drive[49] = 1<<9;
+
+ // Word 50: Reserved
+ BX_SELECTED_DRIVE(channel).id_drive[50] = 0;
+
+ // Word 51: 15-8 PIO data transfer cycle timing mode
+ // 7-0 Vendor unique
+ BX_SELECTED_DRIVE(channel).id_drive[51] = 0x200;
+
+ // Word 52: 15-8 DMA data transfer cycle timing mode
+ // 7-0 Vendor unique
+ BX_SELECTED_DRIVE(channel).id_drive[52] = 0x200;
+
+ // Word 53: 15-1 Reserved
+ // 0 1=the fields reported in words 54-58 are valid
+ // 0=the fields reported in words 54-58 may be valid
+ BX_SELECTED_DRIVE(channel).id_drive[53] = 0;
+
+ // Word 54: # of user-addressable cylinders in curr xlate mode
+ // Word 55: # of user-addressable heads in curr xlate mode
+ // Word 56: # of user-addressable sectors/track in curr xlate mode
+ BX_SELECTED_DRIVE(channel).id_drive[54] = BX_SELECTED_DRIVE(channel).hard_drive->cylinders;
+ BX_SELECTED_DRIVE(channel).id_drive[55] = BX_SELECTED_DRIVE(channel).hard_drive->heads;
+ BX_SELECTED_DRIVE(channel).id_drive[56] = BX_SELECTED_DRIVE(channel).hard_drive->sectors;
+
+ // Word 57-58: Current capacity in sectors
+ // Excludes all sectors used for device specific purposes.
+ temp32 =
+ BX_SELECTED_DRIVE(channel).hard_drive->cylinders *
+ BX_SELECTED_DRIVE(channel).hard_drive->heads *
+ BX_SELECTED_DRIVE(channel).hard_drive->sectors;
+ BX_SELECTED_DRIVE(channel).id_drive[57] = (temp32 & 0xffff); // LSW
+ BX_SELECTED_DRIVE(channel).id_drive[58] = (temp32 >> 16); // MSW
+
+ // Word 59: 15-9 Reserved
+ // 8 1=multiple sector setting is valid
+ // 7-0 current setting for number of sectors that can be
+ // transferred per interrupt on R/W multiple commands
+ BX_SELECTED_DRIVE(channel).id_drive[59] = 0x0000 | curr_multiple_sectors;
+
+ // Word 60-61:
+ // If drive supports LBA Mode, these words reflect total # of user
+ // addressable sectors. This value does not depend on the current
+ // drive geometry. If the drive does not support LBA mode, these
+ // words shall be set to 0.
+ Bit32u num_sects = BX_SELECTED_DRIVE(channel).hard_drive->cylinders * BX_SELECTED_DRIVE(channel).hard_drive->heads * BX_SELECTED_DRIVE(channel).hard_drive->sectors;
+ BX_SELECTED_DRIVE(channel).id_drive[60] = num_sects & 0xffff; // LSW
+ BX_SELECTED_DRIVE(channel).id_drive[61] = num_sects >> 16; // MSW
+
+ // Word 62: 15-8 single word DMA transfer mode active
+ // 7-0 single word DMA transfer modes supported
+ // The low order byte identifies by bit, all the Modes which are
+ // supported e.g., if Mode 0 is supported bit 0 is set.
+ // The high order byte contains a single bit set to indiciate
+ // which mode is active.
+ BX_SELECTED_DRIVE(channel).id_drive[62] = 0x0;
+
+ // Word 63: 15-8 multiword DMA transfer mode active
+ // 7-0 multiword DMA transfer modes supported
+ // The low order byte identifies by bit, all the Modes which are
+ // supported e.g., if Mode 0 is supported bit 0 is set.
+ // The high order byte contains a single bit set to indiciate
+ // which mode is active.
+ BX_SELECTED_DRIVE(channel).id_drive[63] = 0x0;
+
+ // Word 64-79 Reserved
+ for (i=64; i<=79; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // Word 80: 15-5 reserved
+ // 4 supports ATA/ATAPI-4
+ // 3 supports ATA-3
+ // 2 supports ATA-2
+ // 1 supports ATA-1
+ // 0 reserved
+ BX_SELECTED_DRIVE(channel).id_drive[80] = (1 << 2) | (1 << 1);
+
+ // Word 81: Minor version number
+ BX_SELECTED_DRIVE(channel).id_drive[81] = 0;
+
+ // Word 82: 15 obsolete
+ // 14 NOP command supported
+ // 13 READ BUFFER command supported
+ // 12 WRITE BUFFER command supported
+ // 11 obsolete
+ // 10 Host protected area feature set supported
+ // 9 DEVICE RESET command supported
+ // 8 SERVICE interrupt supported
+ // 7 release interrupt supported
+ // 6 look-ahead supported
+ // 5 write cache supported
+ // 4 supports PACKET command feature set
+ // 3 supports power management feature set
+ // 2 supports removable media feature set
+ // 1 supports securite mode feature set
+ // 0 support SMART feature set
+ BX_SELECTED_DRIVE(channel).id_drive[82] = 1 << 14;
+ BX_SELECTED_DRIVE(channel).id_drive[83] = 1 << 14;
+ BX_SELECTED_DRIVE(channel).id_drive[84] = 1 << 14;
+ BX_SELECTED_DRIVE(channel).id_drive[85] = 1 << 14;
+ BX_SELECTED_DRIVE(channel).id_drive[86] = 0;
+ BX_SELECTED_DRIVE(channel).id_drive[87] = 1 << 14;
+
+ for (i=88; i<=127; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // Word 128-159 Vendor unique
+ for (i=128; i<=159; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+ // Word 160-255 Reserved
+ for (i=160; i<=255; i++)
+ BX_SELECTED_DRIVE(channel).id_drive[i] = 0;
+
+#endif
+
+ BX_DEBUG(("Drive ID Info. initialized : %04d {%s}", 512, BX_SELECTED_TYPE_STRING(channel)));
+
+ // now convert the id_drive array (native 256 word format) to
+ // the controller buffer (512 bytes)
+ for (i=0; i<=255; i++) {
+ temp16 = BX_SELECTED_DRIVE(channel).id_drive[i];
+ BX_SELECTED_CONTROLLER(channel).buffer[i*2] = temp16 & 0x00ff;
+ BX_SELECTED_CONTROLLER(channel).buffer[i*2+1] = temp16 >> 8;
+ }
+}
+
+ void BX_CPP_AttrRegparmN(3)
+bx_hard_drive_c::init_send_atapi_command(Bit8u channel, Bit8u command, int req_length, int alloc_length, bool lazy)
+{
+ // BX_SELECTED_CONTROLLER(channel).byte_count is a union of BX_SELECTED_CONTROLLER(channel).cylinder_no;
+ // lazy is used to force a data read in the buffer at the next read.
+
+ if (BX_SELECTED_CONTROLLER(channel).byte_count == 0xffff)
+ BX_SELECTED_CONTROLLER(channel).byte_count = 0xfffe;
+
+ if ((BX_SELECTED_CONTROLLER(channel).byte_count & 1)
+ && !(alloc_length <= BX_SELECTED_CONTROLLER(channel).byte_count)) {
+ BX_INFO(("Odd byte count (0x%04x) to ATAPI command 0x%02x, using 0x%04x",
+ BX_SELECTED_CONTROLLER(channel).byte_count, command, BX_SELECTED_CONTROLLER(channel).byte_count - 1));
+ BX_SELECTED_CONTROLLER(channel).byte_count -= 1;
+ }
+
+ if (BX_SELECTED_CONTROLLER(channel).byte_count == 0)
+ BX_PANIC(("ATAPI command with zero byte count"));
+
+ if (alloc_length < 0)
+ BX_PANIC(("Allocation length < 0"));
+ if (alloc_length == 0)
+ alloc_length = BX_SELECTED_CONTROLLER(channel).byte_count;
+
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 1;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+
+ // no bytes transfered yet
+ if (lazy)
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 2048;
+ else
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ BX_SELECTED_CONTROLLER(channel).drq_index = 0;
+
+ if (BX_SELECTED_CONTROLLER(channel).byte_count > req_length)
+ BX_SELECTED_CONTROLLER(channel).byte_count = req_length;
+
+ if (BX_SELECTED_CONTROLLER(channel).byte_count > alloc_length)
+ BX_SELECTED_CONTROLLER(channel).byte_count = alloc_length;
+
+ BX_SELECTED_DRIVE(channel).atapi.command = command;
+ BX_SELECTED_DRIVE(channel).atapi.drq_bytes = BX_SELECTED_CONTROLLER(channel).byte_count;
+ BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining = (req_length < alloc_length) ? req_length : alloc_length;
+
+ // if (lazy) {
+ // // bias drq_bytes and total_bytes_remaining
+ // BX_SELECTED_DRIVE(channel).atapi.drq_bytes += 2048;
+ // BX_SELECTED_DRIVE(channel).atapi.total_bytes_remaining += 2048;
+ // }
+}
+
+void
+bx_hard_drive_c::atapi_cmd_error(Bit8u channel, sense_t sense_key, asc_t asc)
+{
+ BX_ERROR(("atapi_cmd_error channel=%02x key=%02x asc=%02x", channel, sense_key, asc));
+
+ BX_SELECTED_CONTROLLER(channel).error_register = sense_key << 4;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.write_fault = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 1;
+
+ BX_SELECTED_DRIVE(channel).sense.sense_key = sense_key;
+ BX_SELECTED_DRIVE(channel).sense.asc = asc;
+ BX_SELECTED_DRIVE(channel).sense.ascq = 0;
+}
+
+void BX_CPP_AttrRegparmN(1)
+bx_hard_drive_c::atapi_cmd_nop(Bit8u channel)
+{
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.i_o = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.c_d = 1;
+ BX_SELECTED_CONTROLLER(channel).interrupt_reason.rel = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.err = 0;
+}
+
+void
+bx_hard_drive_c::init_mode_sense_single(Bit8u channel, const void* src, int size)
+{
+ // Header
+ BX_SELECTED_CONTROLLER(channel).buffer[0] = (size+6) >> 8;
+ BX_SELECTED_CONTROLLER(channel).buffer[1] = (size+6) & 0xff;
+ BX_SELECTED_CONTROLLER(channel).buffer[2] = 0x70; // no media present
+ BX_SELECTED_CONTROLLER(channel).buffer[3] = 0; // reserved
+ BX_SELECTED_CONTROLLER(channel).buffer[4] = 0; // reserved
+ BX_SELECTED_CONTROLLER(channel).buffer[5] = 0; // reserved
+ BX_SELECTED_CONTROLLER(channel).buffer[6] = 0; // reserved
+ BX_SELECTED_CONTROLLER(channel).buffer[7] = 0; // reserved
+
+ // Data
+ memcpy(BX_SELECTED_CONTROLLER(channel).buffer + 8, src, size);
+}
+
+ void BX_CPP_AttrRegparmN(1)
+bx_hard_drive_c::ready_to_send_atapi(Bit8u channel)
+{
+ raise_interrupt(channel);
+}
+
+void BX_CPP_AttrRegparmN(1)
+bx_hard_drive_c::raise_interrupt(Bit8u channel)
+{
+ BX_DEBUG(("raise_interrupt called, disable_irq = %02x", BX_SELECTED_CONTROLLER(channel).control.disable_irq));
+ if (!BX_SELECTED_CONTROLLER(channel).control.disable_irq) { BX_DEBUG(("raising interrupt")); } else { BX_DEBUG(("Not raising interrupt")); }
+ if (!BX_SELECTED_CONTROLLER(channel).control.disable_irq) {
+ Bit32u irq = BX_HD_THIS channels[channel].irq;
+ BX_DEBUG(("Raising interrupt %d {%s}", irq, BX_SELECTED_TYPE_STRING(channel)));
+ DEV_pic_raise_irq(irq);
+ } else {
+ if (bx_dbg.disk || (BX_SELECTED_IS_CD(channel) && bx_dbg.cdrom))
+ BX_INFO(("Interrupt masked {%s}", BX_SELECTED_TYPE_STRING(channel)));
+ }
+}
+
+ void
+bx_hard_drive_c::command_aborted(Bit8u channel, unsigned value)
+{
+ BX_DEBUG(("aborting on command 0x%02x {%s}", value, BX_SELECTED_TYPE_STRING(channel)));
+ BX_SELECTED_CONTROLLER(channel).current_command = 0;
+ BX_SELECTED_CONTROLLER(channel).status.busy = 0;
+ BX_SELECTED_CONTROLLER(channel).status.drive_ready = 1;
+ BX_SELECTED_CONTROLLER(channel).status.err = 1;
+ BX_SELECTED_CONTROLLER(channel).error_register = 0x04; // command ABORTED
+ BX_SELECTED_CONTROLLER(channel).status.drq = 0;
+ BX_SELECTED_CONTROLLER(channel).status.seek_complete = 0;
+ BX_SELECTED_CONTROLLER(channel).status.corrected_data = 0;
+ BX_SELECTED_CONTROLLER(channel).buffer_index = 0;
+ raise_interrupt(channel);
+}
+
+ Bit32u
+bx_hard_drive_c::get_device_handle(Bit8u channel, Bit8u device)
+{
+ BX_DEBUG(("get_device_handle %d %d",channel, device));
+ if ((channel < BX_MAX_ATA_CHANNEL) && (device < 2)) {
+ return ((channel*2) + device);
+ }
+
+ return BX_MAX_ATA_CHANNEL*2;
+}
+
+ Bit32u
+bx_hard_drive_c::get_first_cd_handle(void)
+{
+ for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ if (BX_DRIVE_IS_CD(channel,0)) return (channel*2);
+ if (BX_DRIVE_IS_CD(channel,1)) return ((channel*2) + 1);
+ }
+ return BX_MAX_ATA_CHANNEL*2;
+}
+
+ unsigned
+bx_hard_drive_c::get_cd_media_status(Bit32u handle)
+{
+ if ( handle >= BX_MAX_ATA_CHANNEL*2 ) return 0;
+
+ Bit8u channel = handle / 2;
+ Bit8u device = handle % 2;
+ return( BX_HD_THIS channels[channel].drives[device].cdrom.ready );
+}
+
+ unsigned
+bx_hard_drive_c::set_cd_media_status(Bit32u handle, unsigned status)
+{
+ BX_DEBUG (("set_cd_media_status handle=%d status=%d", handle, status));
+ if ( handle >= BX_MAX_ATA_CHANNEL*2 ) return 0;
+
+ Bit8u channel = handle / 2;
+ Bit8u device = handle % 2;
+
+ // if setting to the current value, nothing to do
+ if (status == BX_HD_THIS channels[channel].drives[device].cdrom.ready)
+ return(status);
+ // return 0 if no cdromd is present
+ if (!BX_DRIVE_IS_CD(channel,device))
+ return(0);
+
+ if (status == 0) {
+ // eject cdrom if not locked by guest OS
+ if (BX_HD_THIS channels[channel].drives[device].cdrom.locked) return(1);
+ else {
+#ifdef LOWLEVEL_CDROM
+ BX_HD_THIS channels[channel].drives[device].cdrom.cd->eject_cdrom();
+#endif
+ BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0;
+ bx_options.atadevice[channel][device].Ostatus->set(BX_EJECTED);
+ }
+ }
+ else {
+ // insert cdrom
+#ifdef LOWLEVEL_CDROM
+ if (BX_HD_THIS channels[channel].drives[device].cdrom.cd->insert_cdrom(bx_options.atadevice[channel][device].Opath->getptr())) {
+ BX_INFO(( "Media present in CD-ROM drive"));
+ BX_HD_THIS channels[channel].drives[device].cdrom.ready = 1;
+ BX_HD_THIS channels[channel].drives[device].cdrom.capacity = BX_HD_THIS channels[channel].drives[device].cdrom.cd->capacity();
+ bx_options.atadevice[channel][device].Ostatus->set(BX_INSERTED);
+ BX_SELECTED_DRIVE(channel).sense.sense_key = SENSE_UNIT_ATTENTION;
+ BX_SELECTED_DRIVE(channel).sense.asc = 0;
+ BX_SELECTED_DRIVE(channel).sense.ascq = 0;
+ raise_interrupt(channel);
+ }
+ else {
+#endif
+ BX_INFO(( "Could not locate CD-ROM, continuing with media not present"));
+ BX_HD_THIS channels[channel].drives[device].cdrom.ready = 0;
+ bx_options.atadevice[channel][device].Ostatus->set(BX_EJECTED);
+#ifdef LOWLEVEL_CDROM
+ }
+#endif
+ }
+ return( BX_HD_THIS channels[channel].drives[device].cdrom.ready );
+}
+
+
+/*** default_image_t function definitions ***/
+
+int default_image_t::open (const char* pathname)
+{
+ return open(pathname, O_RDWR);
+}
+
+int default_image_t::open (const char* pathname, int flags)
+{
+ fd = ::open(pathname, flags
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+
+ if (fd < 0) {
+ return fd;
+ }
+
+ /* look at size of image file to calculate disk geometry */
+ struct stat stat_buf;
+ int ret = fstat(fd, &stat_buf);
+ if (ret) {
+ BX_PANIC(("fstat() returns error!"));
+ }
+
+ return fd;
+}
+
+void default_image_t::close ()
+{
+ if (fd > -1) {
+ ::close(fd);
+ }
+}
+
+off_t default_image_t::lseek (off_t offset, int whence)
+{
+ return ::lseek(fd, offset, whence);
+}
+
+ssize_t default_image_t::read (void* buf, size_t count)
+{
+ return ::read(fd, (char*) buf, count);
+}
+
+ssize_t default_image_t::write (const void* buf, size_t count)
+{
+ return ::write(fd, (char*) buf, count);
+}
+
+char increment_string (char *str, int diff)
+{
+ // find the last character of the string, and increment it.
+ char *p = str;
+ while (*p != 0) p++;
+ BX_ASSERT (p>str); // choke on zero length strings
+ p--; // point to last character of the string
+ (*p) += diff; // increment to next/previous ascii code.
+ BX_DEBUG(("increment string returning '%s'", str));
+ return (*p);
+}
+
+/*** concat_image_t function definitions ***/
+
+concat_image_t::concat_image_t ()
+{
+ fd = -1;
+}
+
+void concat_image_t::increment_string (char *str)
+{
+ ::increment_string(str, +1);
+}
+
+int concat_image_t::open (const char* pathname0)
+{
+ char *pathname = strdup (pathname0);
+ BX_DEBUG(("concat_image_t.open"));
+ off_t start_offset = 0;
+ for (int i=0; i<BX_CONCAT_MAX_IMAGES; i++) {
+ fd_table[i] = ::open(pathname, O_RDWR
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+ if (fd_table[i] < 0) {
+ // open failed.
+ // if no FD was opened successfully, return -1 (fail).
+ if (i==0) return -1;
+ // otherwise, it only means that all images in the series have
+ // been opened. Record the number of fds opened successfully.
+ maxfd = i;
+ break;
+ }
+ BX_DEBUG(("concat_image: open image %s, fd[%d] = %d", pathname, i, fd_table[i]));
+ /* look at size of image file to calculate disk geometry */
+ struct stat stat_buf;
+ int ret = fstat(fd_table[i], &stat_buf);
+ if (ret) {
+ BX_PANIC(("fstat() returns error!"));
+ }
+#ifdef S_ISBLK
+ if (S_ISBLK(stat_buf.st_mode)) {
+ BX_PANIC(("block devices should REALLY NOT be used with --enable-split-hd. "
+ "Please reconfigure with --disable-split-hd"));
+ }
+#endif
+ if ((stat_buf.st_size % 512) != 0) {
+ BX_PANIC(("size of disk image must be multiple of 512 bytes"));
+ }
+ length_table[i] = stat_buf.st_size;
+ start_offset_table[i] = start_offset;
+ start_offset += stat_buf.st_size;
+ increment_string (pathname);
+ }
+ // start up with first image selected
+ index = 0;
+ fd = fd_table[0];
+ thismin = 0;
+ thismax = length_table[0]-1;
+ seek_was_last_op = 0;
+ return 0; // success.
+}
+
+void concat_image_t::close ()
+{
+ BX_DEBUG(("concat_image_t.close"));
+ if (fd > -1) {
+ ::close(fd);
+ }
+}
+
+off_t concat_image_t::lseek (off_t offset, int whence)
+{
+ if ((offset % 512) != 0)
+ BX_PANIC( ("lseek HD with offset not multiple of 512"));
+ BX_DEBUG(("concat_image_t.lseek(%d)", whence));
+ // is this offset in this disk image?
+ if (offset < thismin) {
+ // no, look at previous images
+ for (int i=index-1; i>=0; i--) {
+ if (offset >= start_offset_table[i]) {
+ index = i;
+ fd = fd_table[i];
+ thismin = start_offset_table[i];
+ thismax = thismin + length_table[i] - 1;
+ BX_DEBUG(("concat_image_t.lseek to earlier image, index=%d", index));
+ break;
+ }
+ }
+ } else if (offset > thismax) {
+ // no, look at later images
+ for (int i=index+1; i<maxfd; i++) {
+ if (offset < start_offset_table[i] + length_table[i]) {
+ index = i;
+ fd = fd_table[i];
+ thismin = start_offset_table[i];
+ thismax = thismin + length_table[i] - 1;
+ BX_DEBUG(("concat_image_t.lseek to earlier image, index=%d", index));
+ break;
+ }
+ }
+ }
+ // now offset should be within the current image.
+ offset -= start_offset_table[index];
+ if (offset < 0 || offset >= length_table[index]) {
+ BX_PANIC(("concat_image_t.lseek to byte %ld failed", (long)offset));
+ return -1;
+ }
+
+ seek_was_last_op = 1;
+ return ::lseek(fd, offset, whence);
+}
+
+ssize_t concat_image_t::read (void* buf, size_t count)
+{
+ if (bx_dbg.disk)
+ BX_DEBUG(("concat_image_t.read %ld bytes", (long)count));
+ // notice if anyone does sequential read or write without seek in between.
+ // This can be supported pretty easily, but needs additional checks for
+ // end of a partial image.
+ if (!seek_was_last_op)
+ BX_PANIC( ("no seek before read"));
+ return ::read(fd, (char*) buf, count);
+}
+
+ssize_t concat_image_t::write (const void* buf, size_t count)
+{
+ BX_DEBUG(("concat_image_t.write %ld bytes", (long)count));
+ // notice if anyone does sequential read or write without seek in between.
+ // This can be supported pretty easily, but needs additional checks for
+ // end of a partial image.
+ if (!seek_was_last_op)
+ BX_PANIC( ("no seek before write"));
+ return ::write(fd, (char*) buf, count);
+}
+
+/*** sparse_image_t function definitions ***/
+sparse_image_t::sparse_image_t ()
+{
+ fd = -1;
+ pathname = NULL;
+#ifdef _POSIX_MAPPED_FILES
+ mmap_header = NULL;
+#endif
+ pagetable = NULL;
+}
+
+
+/*
+void showpagetable(uint32 * pagetable, size_t numpages)
+{
+ printf("Non null pages: ");
+ for (int i = 0; i < numpages; i++)
+ {
+ if (pagetable[i] != 0xffffffff)
+ {
+ printf("%d ", i);
+ }
+ }
+ printf("\n");
+}
+*/
+
+
+void sparse_image_t::read_header()
+{
+ BX_ASSERT(sizeof(header) == SPARSE_HEADER_SIZE);
+
+ int ret = ::read(fd, &header, sizeof(header));
+
+ if (-1 == ret)
+ {
+ panic(strerror(errno));
+ }
+
+ if (sizeof(header) != ret)
+ {
+ panic("could not read entire header");
+ }
+
+ if (dtoh32(header.magic) != SPARSE_HEADER_MAGIC)
+ {
+ panic("failed header magic check");
+ }
+
+ if (dtoh32(header.version) != 1)
+ {
+ panic("unknown version in header");
+ }
+
+ pagesize = dtoh32(header.pagesize);
+ uint32 numpages = dtoh32(header.numpages);
+
+ total_size = pagesize;
+ total_size *= numpages;
+
+ pagesize_shift = 0;
+ while ((pagesize >> pagesize_shift) > 1) pagesize_shift++;
+
+ if ((uint32)(1 << pagesize_shift) != pagesize)
+ {
+ panic("failed block size header check");
+ }
+
+ pagesize_mask = pagesize - 1;
+
+ size_t preamble_size = (sizeof(uint32) * numpages) + sizeof(header);
+ data_start = 0;
+ while (data_start < preamble_size) data_start += pagesize;
+
+ bool did_mmap = false;
+
+#ifdef _POSIX_MAPPED_FILES
+// Try to memory map from the beginning of the file (0 is trivially a page multiple)
+ void * mmap_header = mmap(NULL, preamble_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mmap_header == MAP_FAILED)
+ {
+ BX_INFO(("failed to mmap sparse disk file - using conventional file access"));
+ mmap_header = NULL;
+ }
+ else
+ {
+ mmap_length = preamble_size;
+ did_mmap = true;
+ pagetable = ((uint32 *) (((uint8 *) mmap_header) + sizeof(header)));
+
+// system_pagesize = getpagesize();
+ system_pagesize_mask = getpagesize() - 1;
+ }
+#endif
+
+ if (!did_mmap)
+ {
+ pagetable = new uint32[numpages];
+
+ if (pagetable == NULL)
+ {
+ panic("could not allocate memory for sparse disk block table");
+ }
+
+ ret = ::read(fd, pagetable, sizeof(uint32) * numpages);
+
+ if (-1 == ret)
+ {
+ panic(strerror(errno));
+ }
+
+ if ((int)(sizeof(uint32) * numpages) != ret)
+ {
+ panic("could not read entire block table");
+ }
+ }
+}
+
+int sparse_image_t::open (const char* pathname0)
+{
+ pathname = strdup(pathname0);
+ BX_DEBUG(("sparse_image_t.open"));
+
+ fd = ::open(pathname, O_RDWR
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+
+ if (fd < 0)
+ {
+ // open failed.
+ return -1;
+ }
+ BX_DEBUG(("sparse_image: open image %s", pathname));
+
+ read_header();
+
+ struct stat stat_buf;
+ if (0 != fstat(fd, &stat_buf)) panic(("fstat() returns error!"));
+
+ underlying_filesize = stat_buf.st_size;
+
+ if ((underlying_filesize % pagesize) != 0)
+ panic("size of sparse disk image is not multiple of page size");
+
+ underlying_current_filepos = 0;
+ if (-1 == ::lseek(fd, 0, SEEK_SET))
+ panic("error while seeking to start of file");
+
+ lseek(0, SEEK_SET);
+
+ //showpagetable(pagetable, header.numpages);
+
+ char * parentpathname = strdup(pathname);
+ char lastchar = ::increment_string(parentpathname, -1);
+
+ if ((lastchar >= '0') && (lastchar <= '9'))
+ {
+ struct stat stat_buf;
+ if (0 == stat(parentpathname, &stat_buf))
+ {
+ parent_image = new sparse_image_t();
+ int ret = parent_image->open(parentpathname);
+ if (ret != 0) return ret;
+ if ( (parent_image->pagesize != pagesize)
+ || (parent_image->total_size != total_size))
+ {
+ panic("child drive image does not have same page count/page size configuration");
+ }
+ }
+ }
+
+ if (parentpathname != NULL) free(parentpathname);
+
+ return 0; // success.
+}
+
+void sparse_image_t::close ()
+{
+ BX_DEBUG(("concat_image_t.close"));
+ if (pathname != NULL)
+ {
+ free(pathname);
+ }
+#ifdef _POSIX_MAPPED_FILES
+ if (mmap_header != NULL)
+ {
+ int ret = munmap(mmap_header, mmap_length);
+ if (ret != 0)
+ BX_INFO(("failed to un-memory map sparse disk file"));
+ }
+ pagetable = NULL; // We didn't malloc it
+#endif
+ if (fd > -1) {
+ ::close(fd);
+ }
+ if (pagetable != NULL)
+ {
+ delete [] pagetable;
+ }
+ if (parent_image != NULL)
+ {
+ delete parent_image;
+ }
+}
+
+off_t sparse_image_t::lseek (off_t offset, int whence)
+{
+ //showpagetable(pagetable, header.numpages);
+
+ if ((offset % 512) != 0)
+ BX_PANIC( ("lseek HD with offset not multiple of 512"));
+ if (whence != SEEK_SET)
+ BX_PANIC( ("lseek HD with whence not SEEK_SET"));
+
+ BX_DEBUG(("sparse_image_t.lseek(%d)", whence));
+
+ if (offset > total_size)
+ {
+ BX_PANIC(("sparse_image_t.lseek to byte %ld failed", (long)offset));
+ return -1;
+ }
+
+ //printf("Seeking to position %ld\n", (long) offset);
+
+ set_virtual_page(offset >> pagesize_shift);
+ position_page_offset = offset & pagesize_mask;
+
+ return 0;
+}
+
+inline off_t sparse_image_t::get_physical_offset()
+{
+ off_t physical_offset = data_start;
+ physical_offset += (position_physical_page << pagesize_shift);
+ physical_offset += position_page_offset;
+
+ return physical_offset;
+}
+
+inline void sparse_image_t::set_virtual_page(uint32 new_virtual_page)
+{
+ position_virtual_page = new_virtual_page;
+
+ position_physical_page = dtoh32(pagetable[position_virtual_page]);
+}
+
+ssize_t sparse_image_t::read_page_fragment(uint32 read_virtual_page, uint32 read_page_offset, size_t read_size, void * buf)
+{
+ if (read_virtual_page != position_virtual_page)
+ {
+ set_virtual_page(read_virtual_page);
+ }
+
+ position_page_offset = read_page_offset;
+
+ if (position_physical_page == SPARSE_PAGE_NOT_ALLOCATED)
+ {
+ if (parent_image != NULL)
+ {
+ return parent_image->read_page_fragment(read_virtual_page, read_page_offset, read_size, buf);
+ }
+ else
+ {
+ memset(buf, read_size, 0);
+ }
+ }
+ else
+ {
+ off_t physical_offset = get_physical_offset();
+
+ if (physical_offset != underlying_current_filepos)
+ {
+ int ret = ::lseek(fd, physical_offset, SEEK_SET);
+ // underlying_current_filepos update deferred
+ if (ret == -1)
+ panic(strerror(errno));
+ }
+
+ //printf("Reading %s at position %ld size %d\n", pathname, (long) physical_offset, (long) read_size);
+ ssize_t readret = ::read(fd, buf, read_size);
+
+ if (readret == -1)
+ {
+ panic(strerror(errno));
+ }
+
+ if ((size_t)readret != read_size)
+ {
+ panic("could not read block contents from file");
+ }
+
+ underlying_current_filepos = physical_offset + read_size;
+ }
+
+ return read_size;
+}
+
+ssize_t sparse_image_t::read(void* buf, size_t count)
+{
+ //showpagetable(pagetable, header.numpages);
+ ssize_t total_read = 0;
+
+ if (bx_dbg.disk)
+ BX_DEBUG(("sparse_image_t.read %ld bytes", (long)count));
+
+ while (count != 0)
+ {
+ size_t can_read = pagesize - position_page_offset;
+ if (count < can_read) can_read = count;
+
+ BX_ASSERT (can_read != 0);
+
+ size_t was_read = read_page_fragment(position_virtual_page, position_page_offset, can_read, buf);
+
+ BX_ASSERT(was_read == can_read);
+
+ total_read += can_read;
+
+ position_page_offset += can_read;
+ if (position_page_offset == pagesize)
+ {
+ position_page_offset = 0;
+ set_virtual_page(position_virtual_page + 1);
+ }
+
+ BX_ASSERT(position_page_offset < pagesize);
+
+ buf = (((uint8 *) buf) + can_read);
+ count -= can_read;
+ }
+
+ return total_read;
+}
+
+void sparse_image_t::panic(const char * message)
+{
+ char buffer[1024];
+ if (message == NULL)
+ {
+ snprintf(buffer, sizeof(buffer), "error with sparse disk image %s", pathname);
+ }
+ else
+ {
+ snprintf(buffer, sizeof(buffer), "error with sparse disk image %s - %s", pathname, message);
+ }
+ BX_PANIC((buffer));
+}
+
+ssize_t sparse_image_t::write (const void* buf, size_t count)
+{
+ //showpagetable(pagetable, header.numpages);
+
+ ssize_t total_written = 0;
+
+ uint32 update_pagetable_start = position_virtual_page;
+ uint32 update_pagetable_count = 0;
+
+ if (bx_dbg.disk)
+ BX_DEBUG(("sparse_image_t.write %ld bytes", (long)count));
+
+ while (count != 0)
+ {
+ size_t can_write = pagesize - position_page_offset;
+ if (count < can_write) can_write = count;
+
+ BX_ASSERT (can_write != 0);
+
+ if (position_physical_page == SPARSE_PAGE_NOT_ALLOCATED)
+ {
+ // We just add on another page at the end of the file
+ // Reclamation, compaction etc should currently be done off-line
+
+ size_t data_size = underlying_filesize - data_start;
+ BX_ASSERT((data_size % pagesize) == 0);
+
+
+ uint32 data_size_pages = data_size / pagesize;
+ uint32 next_data_page = data_size_pages;
+
+ pagetable[position_virtual_page] = htod32(next_data_page);
+ position_physical_page = next_data_page;
+
+ off_t page_file_start = data_start + (position_physical_page << pagesize_shift);
+
+ if (parent_image != NULL)
+ {
+ // If we have a parent, we must merge our portion with the parent
+ void * writebuffer = NULL;
+
+ if (can_write == pagesize)
+ {
+ writebuffer = (void *) buf;
+ }
+ else
+ {
+ writebuffer = malloc(pagesize);
+ if (writebuffer == NULL)
+ panic("Cannot allocate sufficient memory for page-merge in write");
+
+ // Read entire page - could optimize, but simple for now
+ parent_image->read_page_fragment(position_virtual_page, 0, pagesize, writebuffer);
+
+ void * dest_start = ((uint8 *) writebuffer) + position_page_offset;
+ memcpy(dest_start, buf, can_write);
+ }
+
+ int ret;
+ ret = ::lseek(fd, page_file_start, SEEK_SET);
+ // underlying_current_filepos update deferred
+ if (-1 == ret) panic(strerror(errno));
+
+ ret = ::write(fd, writebuffer, pagesize);
+
+ if (-1 == ret) panic(strerror(errno));
+
+ if (pagesize != (uint32)ret) panic("failed to write entire merged page to disk");
+
+ if (can_write != pagesize)
+ {
+ free(writebuffer);
+ }
+ }
+ else
+ {
+ // We need to write a zero page because read has been returning zeroes
+ // We seek as close to the page end as possible, and then write a little
+ // This produces a sparse file which has blanks
+ // Also very quick, even when pagesize is massive
+ int ret;
+ ret = ::lseek(fd, page_file_start + pagesize - 4, SEEK_SET);
+ // underlying_current_filepos update deferred
+ if (-1 == ret) panic(strerror(errno));
+
+ uint32 zero = 0;
+ ret = ::write(fd, &zero, 4);
+
+ if (-1 == ret) panic(strerror(errno));
+
+ if (4 != ret) panic("failed to write entire blank page to disk");
+ }
+
+ update_pagetable_count = (position_virtual_page - update_pagetable_start) + 1;
+ underlying_filesize = underlying_current_filepos = page_file_start + pagesize;
+ }
+
+ BX_ASSERT(position_physical_page != SPARSE_PAGE_NOT_ALLOCATED);
+
+ off_t physical_offset = get_physical_offset();
+
+ if (physical_offset != underlying_current_filepos)
+ {
+ int ret = ::lseek(fd, physical_offset, SEEK_SET);
+ // underlying_current_filepos update deferred
+ if (ret == -1)
+ panic(strerror(errno));
+ }
+
+ //printf("Writing at position %ld size %d\n", (long) physical_offset, can_write);
+ ssize_t writeret = ::write(fd, buf, can_write);
+
+ if (writeret == -1)
+ {
+ panic(strerror(errno));
+ }
+
+ if ((size_t)writeret != can_write)
+ {
+ panic("could not write block contents to file");
+ }
+
+ underlying_current_filepos = physical_offset + can_write;
+
+ total_written += can_write;
+
+ position_page_offset += can_write;
+ if (position_page_offset == pagesize)
+ {
+ position_page_offset = 0;
+ set_virtual_page(position_virtual_page + 1);
+ }
+
+ BX_ASSERT(position_page_offset < pagesize);
+
+ buf = (((uint8 *) buf) + can_write);
+ count -= can_write;
+ }
+
+ if (update_pagetable_count != 0)
+ {
+ bool done = false;
+ off_t pagetable_write_from = sizeof(header) + (sizeof(uint32) * update_pagetable_start);
+ size_t write_bytecount = update_pagetable_count * sizeof(uint32);
+
+#ifdef _POSIX_MAPPED_FILES
+ if (mmap_header != NULL)
+ {
+ // Sync from the beginning of the page
+ size_t system_page_offset = pagetable_write_from & system_pagesize_mask;
+ void * start = ((uint8 *) mmap_header + pagetable_write_from - system_page_offset);
+
+ int ret = msync(start, system_page_offset + write_bytecount, MS_ASYNC);
+
+ if (ret != 0)
+ panic(strerror(errno));
+
+ done = true;
+ }
+#endif
+
+ if (!done)
+ {
+ int ret = ::lseek(fd, pagetable_write_from, SEEK_SET);
+ // underlying_current_filepos update deferred
+ if (ret == -1) panic(strerror(errno));
+
+ //printf("Writing header at position %ld size %ld\n", (long) pagetable_write_from, (long) write_bytecount);
+ ret = ::write(fd, &pagetable[update_pagetable_start], write_bytecount);
+ if (ret == -1) panic(strerror(errno));
+ if ((size_t)ret != write_bytecount) panic("could not write entire updated block header");
+
+ underlying_current_filepos = pagetable_write_from + write_bytecount;
+ }
+ }
+
+ return total_written;
+}
+
+#if DLL_HD_SUPPORT
+/*** dll_image_t function definitions ***/
+
+/*
+function vdisk_open(path:PChar;numclusters,clustersize:integer):integer;
+procedure vdisk_read(vunit:integer;blk:integer;var buf:TBlock);
+procedure vdisk_write(vunit:integer;blk:integer;var buf:TBlock);
+procedure vdisk_close(vunit:integer);
+*/
+
+HINSTANCE hlib_vdisk = 0;
+
+int (*vdisk_open) (const char *path,int numclusters,int clustersize);
+void (*vdisk_read) (int vunit,int blk,void *buf);
+void (*vdisk_write) (int vunit,int blk,const void *buf);
+void (*vdisk_close) (int vunit);
+
+int dll_image_t::open (const char* pathname)
+{
+ if (hlib_vdisk == 0) {
+ hlib_vdisk = LoadLibrary("vdisk.dll");
+ if (hlib_vdisk != 0) {
+ vdisk_read = (void (*)(int,int,void*)) GetProcAddress(hlib_vdisk,"vdisk_read");
+ vdisk_write = (void (*)(int,int,const void*)) GetProcAddress(hlib_vdisk,"vdisk_write");
+ vdisk_open = (int (*)(const char *,int,int)) GetProcAddress(hlib_vdisk,"vdisk_open");
+ vdisk_close = (void (*)(int)) GetProcAddress(hlib_vdisk,"vdisk_close");
+ }
+ }
+ if (hlib_vdisk != 0) {
+ vunit = vdisk_open(pathname,0x10000,64);
+ vblk = 0;
+ } else {
+ vunit = -2;
+ }
+ return vunit;
+}
+
+void dll_image_t::close ()
+{
+ if (vunit >= 0 && hlib_vdisk != 0) {
+ vdisk_close(vunit);
+ }
+}
+
+off_t dll_image_t::lseek (off_t offset, int whence)
+{
+ vblk = offset >> 9;
+ return 0;
+}
+
+ssize_t dll_image_t::read (void* buf, size_t count)
+{
+ if (vunit >= 0 && hlib_vdisk != 0) {
+ vdisk_read(vunit,vblk,buf);
+ return count;
+ } else {
+ return -1;
+ }
+}
+
+ssize_t dll_image_t::write (const void* buf, size_t count)
+{
+ if (vunit >= 0 && hlib_vdisk != 0) {
+ vdisk_write(vunit,vblk,buf);
+ return count;
+ } else {
+ return -1;
+ }
+}
+#endif // DLL_HD_SUPPORT
+
+error_recovery_t::error_recovery_t ()
+{
+ if (sizeof(error_recovery_t) != 8) {
+ BX_PANIC(("error_recovery_t has size != 8"));
+ }
+
+ data[0] = 0x01;
+ data[1] = 0x06;
+ data[2] = 0x00;
+ data[3] = 0x05; // Try to recover 5 times
+ data[4] = 0x00;
+ data[5] = 0x00;
+ data[6] = 0x00;
+ data[7] = 0x00;
+}
+
+uint16 BX_CPP_AttrRegparmN(1)
+read_16bit(const uint8* buf)
+{
+ return (buf[0] << 8) | buf[1];
+}
+
+uint32 BX_CPP_AttrRegparmN(1)
+read_32bit(const uint8* buf)
+{
+ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+}
+
+// redolog implementation
+redolog_t::redolog_t ()
+{
+ fd = -1;
+ catalog = NULL;
+ bitmap = NULL;
+ extent_index = (Bit32u)0;
+ extent_offset = (Bit32u)0;
+ extent_next = (Bit32u)0;
+}
+
+void
+redolog_t::print_header()
+{
+ BX_INFO(("redolog : Standard Header : magic='%s', type='%s', subtype='%s', version = %d.%d",
+ header.standard.magic, header.standard.type, header.standard.subtype,
+ dtoh32(header.standard.version)/0x10000,
+ dtoh32(header.standard.version)%0x10000));
+ BX_INFO(("redolog : Specific Header : #entries=%d, bitmap size=%d, exent size = %d disk size = %lld",
+ dtoh32(header.specific.catalog),
+ dtoh32(header.specific.bitmap),
+ dtoh32(header.specific.extent),
+ dtoh64(header.specific.disk)));
+}
+
+int
+redolog_t::make_header (const char* type, Bit64u size)
+{
+ Bit32u entries, extent_size, bitmap_size;
+ Bit64u maxsize;
+ Bit32u flip=0;
+
+ // Set standard header values
+ strcpy((char*)header.standard.magic, STANDARD_HEADER_MAGIC);
+ strcpy((char*)header.standard.type, REDOLOG_TYPE);
+ strcpy((char*)header.standard.subtype, type);
+ header.standard.version = htod32(STANDARD_HEADER_VERSION);
+ header.standard.header = htod32(STANDARD_HEADER_SIZE);
+
+ entries = 512;
+ bitmap_size = 1;
+
+ // Compute #entries and extent size values
+ do {
+ extent_size = 8 * bitmap_size * 512;
+
+ header.specific.catalog = htod32(entries);
+ header.specific.bitmap = htod32(bitmap_size);
+ header.specific.extent = htod32(extent_size);
+
+ maxsize = (Bit64u)entries * (Bit64u)extent_size;
+
+ flip++;
+
+ if(flip&0x01) bitmap_size *= 2;
+ else entries *= 2;
+ } while (maxsize < size);
+
+ header.specific.disk = htod64(size);
+
+ print_header();
+
+ catalog = (Bit32u*)malloc(dtoh32(header.specific.catalog) * sizeof(Bit32u));
+ bitmap = (Bit8u*)malloc(dtoh32(header.specific.bitmap));
+
+ if ((catalog == NULL) || (bitmap==NULL))
+ BX_PANIC(("redolog : could not malloc catalog or bitmap"));
+
+ for (Bit32u i=0; i<dtoh32(header.specific.catalog); i++)
+ catalog[i] = htod32(REDOLOG_PAGE_NOT_ALLOCATED);
+
+ bitmap_blocs = 1 + (dtoh32(header.specific.bitmap) - 1) / 512;
+ extent_blocs = 1 + (dtoh32(header.specific.extent) - 1) / 512;
+
+ BX_DEBUG(("redolog : each bitmap is %d blocs", bitmap_blocs));
+ BX_DEBUG(("redolog : each extent is %d blocs", extent_blocs));
+
+ return 0;
+}
+
+int
+redolog_t::create (const char* filename, const char* type, Bit64u size)
+{
+ int filedes;
+
+ BX_INFO(("redolog : creating redolog %s", filename));
+
+ filedes = ::open(filename, O_RDWR | O_CREAT | O_TRUNC
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ , S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP);
+
+ return create(filedes, type, size);
+}
+
+int
+redolog_t::create (int filedes, const char* type, Bit64u size)
+{
+ fd = filedes;
+
+ if (fd < 0)
+ {
+ // open failed.
+ return -1;
+ }
+
+ if (make_header(type, size) < 0)
+ {
+ return -1;
+ }
+
+ // Write header
+ ::write(fd, &header, dtoh32(header.standard.header));
+
+ // Write catalog
+ // FIXME could mmap
+ ::write(fd, catalog, dtoh32(header.specific.catalog) * sizeof (Bit32u));
+
+ return 0;
+}
+
+int
+redolog_t::open (const char* filename, const char *type, Bit64u size)
+{
+ int res;
+
+ fd = ::open(filename, O_RDWR
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+ if (fd < 0)
+ {
+ BX_INFO(("redolog : could not open image %s", filename));
+ // open failed.
+ return -1;
+ }
+ BX_INFO(("redolog : open image %s", filename));
+
+ res = ::read(fd, &header, sizeof(header));
+ if (res != STANDARD_HEADER_SIZE)
+ {
+ BX_PANIC(("redolog : could not read header"));
+ return -1;
+ }
+
+ print_header();
+
+ if (strcmp((char*)header.standard.magic, STANDARD_HEADER_MAGIC) != 0)
+ {
+ BX_PANIC(("redolog : Bad header magic"));
+ return -1;
+ }
+
+ if (strcmp((char*)header.standard.type, REDOLOG_TYPE) != 0)
+ {
+ BX_PANIC(("redolog : Bad header type"));
+ return -1;
+ }
+ if (strcmp((char*)header.standard.subtype, type) != 0)
+ {
+ BX_PANIC(("redolog : Bad header subtype"));
+ return -1;
+ }
+
+ if (dtoh32(header.standard.version) != STANDARD_HEADER_VERSION)
+ {
+ BX_PANIC(("redolog : Bad header version"));
+ return -1;
+ }
+
+ catalog = (Bit32u*)malloc(dtoh32(header.specific.catalog) * sizeof(Bit32u));
+
+ // FIXME could mmap
+ ::lseek(fd,dtoh32(header.standard.header),SEEK_SET);
+ res = ::read(fd, catalog, dtoh32(header.specific.catalog) * sizeof(Bit32u)) ;
+
+ if (res != (ssize_t)(dtoh32(header.specific.catalog) * sizeof(Bit32u)))
+ {
+ BX_PANIC(("redolog : could not read catalog %d=%d",res, dtoh32(header.specific.catalog)));
+ return -1;
+ }
+
+ // check last used extent
+ extent_next = 0;
+ for (Bit32u i=0; i < dtoh32(header.specific.catalog); i++)
+ {
+ if (dtoh32(catalog[i]) != REDOLOG_PAGE_NOT_ALLOCATED)
+ {
+ if (dtoh32(catalog[i]) >= extent_next)
+ extent_next = dtoh32(catalog[i]) + 1;
+ }
+ }
+ BX_INFO(("redolog : next extent will be at index %d",extent_next));
+
+ // memory used for storing bitmaps
+ bitmap = (Bit8u *)malloc(dtoh32(header.specific.bitmap));
+
+ bitmap_blocs = 1 + (dtoh32(header.specific.bitmap) - 1) / 512;
+ extent_blocs = 1 + (dtoh32(header.specific.extent) - 1) / 512;
+
+ BX_DEBUG(("redolog : each bitmap is %d blocs", bitmap_blocs));
+ BX_DEBUG(("redolog : each extent is %d blocs", extent_blocs));
+
+ return 0;
+}
+
+void
+redolog_t::close ()
+{
+ if (fd >= 0)
+ ::close(fd);
+
+ if (catalog != NULL)
+ free(catalog);
+
+ if (bitmap != NULL)
+ free(bitmap);
+}
+
+off_t
+redolog_t::lseek (off_t offset, int whence)
+{
+ if ((offset % 512) != 0) {
+ BX_PANIC( ("redolog : lseek HD with offset not multiple of 512"));
+ return -1;
+ }
+ if (whence != SEEK_SET) {
+ BX_PANIC( ("redolog : lseek HD with whence not SEEK_SET"));
+ return -1;
+ }
+ if (offset > (off_t)dtoh64(header.specific.disk))
+ {
+ BX_PANIC(("redolog : lseek to byte %ld failed", (long)offset));
+ return -1;
+ }
+
+ extent_index = offset / dtoh32(header.specific.extent);
+ extent_offset = (offset % dtoh32(header.specific.extent)) / 512;
+
+ BX_DEBUG(("redolog : lseeking extent index %d, offset %d",extent_index, extent_offset));
+
+ return offset;
+}
+
+ssize_t
+redolog_t::read (void* buf, size_t count)
+{
+ off_t bloc_offset, bitmap_offset;
+
+ if (count != 512)
+ BX_PANIC( ("redolog : read HD with count not 512"));
+
+ BX_DEBUG(("redolog : reading index %d, mapping to %d", extent_index, dtoh32(catalog[extent_index])));
+
+ if (dtoh32(catalog[extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED)
+ {
+ // page not allocated
+ return 0;
+ }
+
+ bitmap_offset = (off_t)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u));
+ bitmap_offset += (off_t)512 * dtoh32(catalog[extent_index]) * (extent_blocs + bitmap_blocs);
+ bloc_offset = bitmap_offset + ((off_t)512 * (bitmap_blocs + extent_offset));
+
+ BX_DEBUG(("redolog : bitmap offset is %x", (Bit32u)bitmap_offset));
+ BX_DEBUG(("redolog : bloc offset is %x", (Bit32u)bloc_offset));
+
+
+ // FIXME if same extent_index as before we can skip bitmap read
+
+ ::lseek(fd, bitmap_offset, SEEK_SET);
+
+ if (::read(fd, bitmap, dtoh32(header.specific.bitmap)) != (ssize_t)dtoh32(header.specific.bitmap))
+ {
+ BX_PANIC(("redolog : failed to read bitmap for extent %d", extent_index));
+ return 0;
+ }
+
+ if ( ((bitmap[extent_offset/8] >> (extent_offset%8)) & 0x01) == 0x00 )
+ {
+ BX_DEBUG(("read not in redolog"));
+
+ // bitmap says bloc not in reloglog
+ return 0;
+ }
+
+ ::lseek(fd, bloc_offset, SEEK_SET);
+
+ return (::read(fd, buf, count));
+}
+
+ssize_t
+redolog_t::write (const void* buf, size_t count)
+{
+ Bit32u i;
+ off_t bloc_offset, bitmap_offset, catalog_offset;
+ ssize_t written;
+ bx_bool update_catalog = 0;
+
+ if (count != 512)
+ BX_PANIC( ("redolog : write HD with count not 512"));
+
+ BX_DEBUG(("redolog : writing index %d, mapping to %d", extent_index, dtoh32(catalog[extent_index])));
+ if (dtoh32(catalog[extent_index]) == REDOLOG_PAGE_NOT_ALLOCATED)
+ {
+ if(extent_next >= dtoh32(header.specific.catalog))
+ {
+ BX_PANIC(("redolog : can't allocate new extent... catalog is full"));
+ return 0;
+ }
+
+ BX_DEBUG(("redolog : allocating new extent at %d", extent_next));
+
+ // Extent not allocated, allocate new
+ catalog[extent_index] = htod32(extent_next);
+
+ extent_next += 1;
+
+ char *zerobuffer = (char*)malloc(512);
+ memset(zerobuffer, 0, 512);
+
+ // Write bitmap
+ bitmap_offset = (off_t)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u));
+ bitmap_offset += (off_t)512 * dtoh32(catalog[extent_index]) * (extent_blocs + bitmap_blocs);
+ ::lseek(fd, bitmap_offset, SEEK_SET);
+ for(i=0; i<bitmap_blocs; i++)
+ {
+ ::write(fd, zerobuffer, 512);
+ }
+ // Write extent
+ for(i=0; i<extent_blocs; i++)
+ {
+ ::write(fd, zerobuffer, 512);
+ }
+
+ free(zerobuffer);
+
+ update_catalog = 1;
+ }
+
+ bitmap_offset = (off_t)STANDARD_HEADER_SIZE + (dtoh32(header.specific.catalog) * sizeof(Bit32u));
+ bitmap_offset += (off_t)512 * dtoh32(catalog[extent_index]) * (extent_blocs + bitmap_blocs);
+ bloc_offset = bitmap_offset + ((off_t)512 * (bitmap_blocs + extent_offset));
+
+ BX_DEBUG(("redolog : bitmap offset is %x", (Bit32u)bitmap_offset));
+ BX_DEBUG(("redolog : bloc offset is %x", (Bit32u)bloc_offset));
+
+ // Write bloc
+ ::lseek(fd, bloc_offset, SEEK_SET);
+ written = ::write(fd, buf, count);
+
+ // Write bitmap
+ // FIXME if same extent_index as before we can skip bitmap read
+ ::lseek(fd, bitmap_offset, SEEK_SET);
+ if (::read(fd, bitmap, dtoh32(header.specific.bitmap)) != (ssize_t)dtoh32(header.specific.bitmap))
+ {
+ BX_PANIC(("redolog : failed to read bitmap for extent %d", extent_index));
+ return 0;
+ }
+
+ // If bloc does not belong to extent yet
+ if ( ((bitmap[extent_offset/8] >> (extent_offset%8)) & 0x01) == 0x00 )
+ {
+ bitmap[extent_offset/8] |= 1 << (extent_offset%8);
+ ::lseek(fd, bitmap_offset, SEEK_SET);
+ ::write(fd, bitmap, dtoh32(header.specific.bitmap));
+ }
+
+ // Write catalog
+ if (update_catalog)
+ {
+ // FIXME if mmap
+ catalog_offset = (off_t)STANDARD_HEADER_SIZE + (extent_index * sizeof(Bit32u));
+
+ BX_DEBUG(("redolog : writing catalog at offset %x", (Bit32u)catalog_offset));
+
+ ::lseek(fd, catalog_offset, SEEK_SET);
+ ::write(fd, &catalog[extent_index], sizeof(Bit32u));
+ }
+
+ return written;
+}
+
+
+/*** growing_image_t function definitions ***/
+
+growing_image_t::growing_image_t(Bit64u _size)
+{
+ redolog = new redolog_t();
+ size = _size;
+}
+
+int growing_image_t::open (const char* pathname)
+{
+ int filedes = redolog->open(pathname,REDOLOG_SUBTYPE_GROWING,size);
+ BX_INFO(("'growing' disk opened, growing file is '%s'", pathname));
+ return filedes;
+}
+
+void growing_image_t::close ()
+{
+ redolog->close();
+}
+
+off_t growing_image_t::lseek (off_t offset, int whence)
+{
+ return redolog->lseek(offset, whence);
+}
+
+ssize_t growing_image_t::read (void* buf, size_t count)
+{
+ memset(buf, 0, count);
+ redolog->read((char*) buf, count);
+ return count;
+}
+
+ssize_t growing_image_t::write (const void* buf, size_t count)
+{
+ return redolog->write((char*) buf, count);
+}
+
+
+/*** undoable_image_t function definitions ***/
+
+undoable_image_t::undoable_image_t(Bit64u _size, const char* _redolog_name)
+{
+ redolog = new redolog_t();
+ ro_disk = new default_image_t();
+ size = _size;
+ redolog_name = NULL;
+ if (_redolog_name != NULL) {
+ if (strcmp(_redolog_name,"") != 0) {
+ redolog_name = strdup(_redolog_name);
+ }
+ }
+}
+
+int undoable_image_t::open (const char* pathname)
+{
+ char *logname=NULL;
+
+ if (ro_disk->open(pathname, O_RDONLY)<0)
+ return -1;
+
+ // if redolog name was set
+ if ( redolog_name != NULL) {
+ if ( strcmp(redolog_name, "") != 0 ) {
+ logname = (char*)malloc(strlen(redolog_name) + 1);
+ strcpy (logname, redolog_name);
+ }
+ }
+
+ // Otherwise we make up the redolog filename from the pathname
+ if ( logname == NULL) {
+ logname = (char*)malloc(strlen(pathname) + UNDOABLE_REDOLOG_EXTENSION_LENGTH + 1);
+ sprintf (logname, "%s%s", pathname, UNDOABLE_REDOLOG_EXTENSION);
+ }
+
+ if (redolog->open(logname,REDOLOG_SUBTYPE_UNDOABLE,size) < 0)
+ {
+ if (redolog->create(logname, REDOLOG_SUBTYPE_UNDOABLE, size) < 0)
+ {
+ BX_PANIC(("Can't open or create redolog '%s'",logname));
+ return -1;
+ }
+ }
+
+ BX_INFO(("'undoable' disk opened: ro-file is '%s', redolog is '%s'", pathname, logname));
+ free(logname);
+
+ return 0;
+}
+
+void undoable_image_t::close ()
+{
+ redolog->close();
+ ro_disk->close();
+
+ if (redolog_name!=NULL)
+ free(redolog_name);
+}
+
+off_t undoable_image_t::lseek (off_t offset, int whence)
+{
+ redolog->lseek(offset, whence);
+ return ro_disk->lseek(offset, whence);
+}
+
+ssize_t undoable_image_t::read (void* buf, size_t count)
+{
+ // This should be fixed if count != 512
+ if ((size_t)redolog->read((char*) buf, count) != count)
+ return ro_disk->read((char*) buf, count);
+ else
+ return count;
+}
+
+ssize_t undoable_image_t::write (const void* buf, size_t count)
+{
+ return redolog->write((char*) buf, count);
+}
+
+
+/*** volatile_image_t function definitions ***/
+
+volatile_image_t::volatile_image_t(Bit64u _size, const char* _redolog_name)
+{
+ redolog = new redolog_t();
+ ro_disk = new default_image_t();
+ size = _size;
+ redolog_temp = NULL;
+ redolog_name = NULL;
+ if (_redolog_name != NULL) {
+ if (strcmp(_redolog_name,"") != 0) {
+ redolog_name = strdup(_redolog_name);
+ }
+ }
+}
+
+int volatile_image_t::open (const char* pathname)
+{
+ int filedes;
+ const char *logname=NULL;
+
+ if (ro_disk->open(pathname, O_RDONLY)<0)
+ return -1;
+
+ // if redolog name was set
+ if ( redolog_name != NULL) {
+ if ( strcmp(redolog_name, "") != 0 ) {
+ logname = redolog_name;
+ }
+ }
+
+ // otherwise use pathname as template
+ if (logname == NULL) {
+ logname = pathname;
+ }
+
+ redolog_temp = (char*)malloc(strlen(logname) + VOLATILE_REDOLOG_EXTENSION_LENGTH + 1);
+ sprintf (redolog_temp, "%s%s", logname, VOLATILE_REDOLOG_EXTENSION);
+
+ filedes = mkstemp (redolog_temp);
+
+ if (filedes < 0)
+ {
+ BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp));
+ return -1;
+ }
+ if (redolog->create(filedes, REDOLOG_SUBTYPE_VOLATILE, size) < 0)
+ {
+ BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp));
+ return -1;
+ }
+
+#if (!defined(WIN32)) && !BX_WITH_MACOS
+ // on unix it is legal to delete an open file
+ unlink(redolog_temp);
+#endif
+
+ BX_INFO(("'volatile' disk opened: ro-file is '%s', redolog is '%s'", pathname, redolog_temp));
+
+ return 0;
+}
+
+void volatile_image_t::close ()
+{
+ redolog->close();
+ ro_disk->close();
+
+#if defined(WIN32) || BX_WITH_MACOS
+ // on non-unix we have to wait till the file is closed to delete it
+ unlink(redolog_temp);
+#endif
+ if (redolog_temp!=NULL)
+ free(redolog_temp);
+
+ if (redolog_name!=NULL)
+ free(redolog_name);
+}
+
+off_t volatile_image_t::lseek (off_t offset, int whence)
+{
+ redolog->lseek(offset, whence);
+ return ro_disk->lseek(offset, whence);
+}
+
+ssize_t volatile_image_t::read (void* buf, size_t count)
+{
+ // This should be fixed if count != 512
+ if ((size_t)redolog->read((char*) buf, count) != count)
+ return ro_disk->read((char*) buf, count);
+ else
+ return count;
+}
+
+ssize_t volatile_image_t::write (const void* buf, size_t count)
+{
+ return redolog->write((char*) buf, count);
+}
+
+#if BX_COMPRESSED_HD_SUPPORT
+
+/*** z_ro_image_t function definitions ***/
+
+z_ro_image_t::z_ro_image_t()
+{
+ offset = (off_t)0;
+}
+
+int z_ro_image_t::open (const char* pathname)
+{
+ fd = ::open(pathname, O_RDONLY
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+
+ if(fd < 0)
+ {
+ BX_PANIC(("Could not open '%s' file", pathname));
+ return fd;
+ }
+
+ gzfile = gzdopen(fd, "rb");
+}
+
+void z_ro_image_t::close ()
+{
+ if (fd > -1) {
+ gzclose(gzfile);
+ // ::close(fd);
+ }
+}
+
+off_t z_ro_image_t::lseek (off_t _offset, int whence)
+{
+ // Only SEEK_SET supported
+ if (whence != SEEK_SET)
+ {
+ BX_PANIC(("lseek on compressed images : only SEEK_SET supported"));
+ }
+
+ // Seeking is expensive on compressed files, so we do it
+ // only when necessary, at the latest moment
+ offset = _offset;
+
+ return offset;
+}
+
+ssize_t z_ro_image_t::read (void* buf, size_t count)
+{
+ gzseek(gzfile, offset, SEEK_SET);
+ return gzread(gzfile, buf, count);
+}
+
+ssize_t z_ro_image_t::write (const void* buf, size_t count)
+{
+ BX_PANIC(("z_ro_image: write not supported"));
+ return 0;
+}
+
+
+/*** z_undoable_image_t function definitions ***/
+
+z_undoable_image_t::z_undoable_image_t(Bit64u _size, const char* _redolog_name)
+{
+ redolog = new redolog_t();
+ ro_disk = new z_ro_image_t();
+ size = _size;
+
+ redolog_name = NULL;
+ if (_redolog_name != NULL) {
+ if (strcmp(_redolog_name,"") != 0) {
+ redolog_name = strdup(_redolog_name);
+ }
+ }
+}
+
+int z_undoable_image_t::open (const char* pathname)
+{
+ char *logname=NULL;
+
+ if (ro_disk->open(pathname)<0)
+ return -1;
+
+ // If redolog name was set
+ if ( redolog_name != NULL) {
+ if ( strcmp(redolog_name, "") != 0) {
+ logname = (char*)malloc(strlen(redolog_name) + 1);
+ strcpy (logname, redolog_name);
+ }
+ }
+
+ // Otherwise we make up the redolog filename from the pathname
+ if ( logname == NULL) {
+ logname = (char*)malloc(strlen(pathname) + UNDOABLE_REDOLOG_EXTENSION_LENGTH + 1);
+ sprintf (logname, "%s%s", pathname, UNDOABLE_REDOLOG_EXTENSION);
+ }
+
+ if (redolog->open(logname,REDOLOG_SUBTYPE_UNDOABLE,size) < 0)
+ {
+ if (redolog->create(logname, REDOLOG_SUBTYPE_UNDOABLE, size) < 0)
+ {
+ BX_PANIC(("Can't open or create redolog '%s'",logname));
+ return -1;
+ }
+ }
+
+ BX_INFO(("'z-undoable' disk opened, z-ro-file is '%s', redolog is '%s'", pathname, logname));
+ free(logname);
+
+ return 0;
+}
+
+void z_undoable_image_t::close ()
+{
+ redolog->close();
+ ro_disk->close();
+
+ if (redolog_name!=NULL)
+ free(redolog_name);
+}
+
+off_t z_undoable_image_t::lseek (off_t offset, int whence)
+{
+ redolog->lseek(offset, whence);
+ return ro_disk->lseek(offset, whence);
+}
+
+ssize_t z_undoable_image_t::read (void* buf, size_t count)
+{
+ // This should be fixed if count != 512
+ if (redolog->read((char*) buf, count) != count)
+ return ro_disk->read((char*) buf, count);
+ else
+ return count;
+}
+
+ssize_t z_undoable_image_t::write (const void* buf, size_t count)
+{
+ return redolog->write((char*) buf, count);
+}
+
+
+/*** z_volatile_image_t function definitions ***/
+
+z_volatile_image_t::z_volatile_image_t(Bit64u _size, const char* _redolog_name)
+{
+ redolog = new redolog_t();
+ ro_disk = new z_ro_image_t();
+ size = _size;
+
+ redolog_temp = NULL;
+ redolog_name = NULL;
+ if (_redolog_name != NULL) {
+ if (strcmp(_redolog_name,"") != 0) {
+ redolog_name = strdup(_redolog_name);
+ }
+ }
+}
+
+int z_volatile_image_t::open (const char* pathname)
+{
+ int filedes;
+ const char *logname=NULL;
+
+ if (ro_disk->open(pathname)<0)
+ return -1;
+
+ // if redolog name was set
+ if ( redolog_name != NULL) {
+ if ( strcmp(redolog_name, "") !=0 ) {
+ logname = redolog_name;
+ }
+ }
+
+ // otherwise use pathname as template
+ if (logname == NULL) {
+ logname = pathname;
+ }
+
+ redolog_temp = (char*)malloc(strlen(logname) + VOLATILE_REDOLOG_EXTENSION_LENGTH + 1);
+ sprintf (redolog_temp, "%s%s", logname, VOLATILE_REDOLOG_EXTENSION);
+
+ filedes = mkstemp (redolog_temp);
+
+ if (filedes < 0)
+ {
+ BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp));
+ return -1;
+ }
+ if (redolog->create(filedes, REDOLOG_SUBTYPE_VOLATILE, size) < 0)
+ {
+ BX_PANIC(("Can't create volatile redolog '%s'", redolog_temp));
+ return -1;
+ }
+
+#if (!defined(WIN32)) && !BX_WITH_MACOS
+ // on unix it is legal to delete an open file
+ unlink(redolog_temp);
+#endif
+
+ BX_INFO(("'z-volatile' disk opened: z-ro-file is '%s', redolog is '%s'", pathname, redolog_temp));
+
+ return 0;
+}
+
+void z_volatile_image_t::close ()
+{
+ redolog->close();
+ ro_disk->close();
+
+#if defined(WIN32) || BX_WITH_MACOS
+ // on non-unix we have to wait till the file is closed to delete it
+ unlink(redolog_temp);
+#endif
+
+ if (redolog_temp!=NULL)
+ free(redolog_temp);
+
+ if (redolog_name!=NULL)
+ free(redolog_name);
+}
+
+off_t z_volatile_image_t::lseek (off_t offset, int whence)
+{
+ redolog->lseek(offset, whence);
+ return ro_disk->lseek(offset, whence);
+}
+
+ssize_t z_volatile_image_t::read (void* buf, size_t count)
+{
+ // This should be fixed if count != 512
+ if (redolog->read((char*) buf, count) != count)
+ return ro_disk->read((char*) buf, count);
+ else
+ return count;
+}
+
+ssize_t z_volatile_image_t::write (const void* buf, size_t count)
+{
+ return redolog->write((char*) buf, count);
+}
+
+
+#endif
diff --git a/tools/ioemu/iodev/harddrv.h b/tools/ioemu/iodev/harddrv.h
new file mode 100644
index 0000000000..9320e614d5
--- /dev/null
+++ b/tools/ioemu/iodev/harddrv.h
@@ -0,0 +1,765 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: harddrv.h,v 1.22.2.1 2004/02/06 22:14:36 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// SPARSE IMAGES HEADER
+#define SPARSE_HEADER_MAGIC (0x02468ace)
+#define SPARSE_HEADER_VERSION 1
+#define SPARSE_HEADER_SIZE (256) // Plenty of room for later
+#define SPARSE_PAGE_NOT_ALLOCATED (0xffffffff)
+
+ typedef struct
+ {
+ uint32 magic;
+ uint32 version;
+ uint32 pagesize;
+ uint32 numpages;
+
+ uint32 padding[60];
+ } sparse_header_t;
+
+#define STANDARD_HEADER_MAGIC "Bochs Virtual HD Image"
+#define STANDARD_HEADER_VERSION (0x00010000)
+#define STANDARD_HEADER_SIZE (512)
+
+
+ // WARNING : headers are kept in x86 (little) endianness
+ typedef struct
+ {
+ Bit8u magic[32];
+ Bit8u type[16];
+ Bit8u subtype[16];
+ Bit32u version;
+ Bit32u header;
+ } standard_header_t;
+
+#define REDOLOG_TYPE "Redolog"
+#define REDOLOG_SUBTYPE_UNDOABLE "Undoable"
+#define REDOLOG_SUBTYPE_VOLATILE "Volatile"
+#define REDOLOG_SUBTYPE_GROWING "Growing"
+// #define REDOLOG_SUBTYPE_Z_UNDOABLE "z-Undoable"
+// #define REDOLOG_SUBTYPE_Z_VOLATILE "z-Volatile"
+
+#define REDOLOG_PAGE_NOT_ALLOCATED (0xffffffff)
+
+#define UNDOABLE_REDOLOG_EXTENSION ".redolog"
+#define UNDOABLE_REDOLOG_EXTENSION_LENGTH (strlen(UNDOABLE_REDOLOG_EXTENSION))
+#define VOLATILE_REDOLOG_EXTENSION ".XXXXXX"
+#define VOLATILE_REDOLOG_EXTENSION_LENGTH (strlen(VOLATILE_REDOLOG_EXTENSION))
+
+ typedef struct
+ {
+ // the fields in the header are kept in little endian
+ Bit32u catalog; // #entries in the catalog
+ Bit32u bitmap; // bitmap size in bytes
+ Bit32u extent; // extent size in bytes
+ Bit64u disk; // disk size in bytes
+ } redolog_specific_header_t;
+
+ typedef struct
+ {
+ standard_header_t standard;
+ redolog_specific_header_t specific;
+
+ Bit8u padding[STANDARD_HEADER_SIZE - (sizeof (standard_header_t) + sizeof (redolog_specific_header_t))];
+ } redolog_header_t;
+
+// htod : convert host to disk (little) endianness
+// dtoh : convert disk (little) to host endianness
+#if defined (BX_LITTLE_ENDIAN)
+#define htod32(val) (val)
+#define dtoh32(val) (val)
+#define htod64(val) (val)
+#define dtoh64(val) (val)
+#else
+#define htod32(val) ( (((val)&0xff000000)>>24) | (((val)&0xff0000)>>8) | (((val)&0xff00)<<8) | (((val)&0xff)<<24) )
+#define dtoh32(val) htod32(val)
+#define htod64(val) ( (((val)&0xff00000000000000LL)>>56) | (((val)&0xff000000000000LL)>>40) | (((val)&0xff0000000000LL)>>24) | (((val)&0xff00000000LL)>>8) | (((val)&0xff000000LL)<<8) | (((val)&0xff0000LL)<<24) | (((val)&0xff00LL)<<40) | (((val)&0xffLL)<<56) )
+#define dtoh64(val) htod64(val)
+#endif
+
+#ifndef INCLUDE_ONLY_HD_HEADERS
+
+typedef enum _sense {
+ SENSE_NONE = 0, SENSE_NOT_READY = 2, SENSE_ILLEGAL_REQUEST = 5,
+ SENSE_UNIT_ATTENTION = 6
+} sense_t;
+
+typedef enum _asc {
+ ASC_INV_FIELD_IN_CMD_PACKET = 0x24,
+ ASC_MEDIUM_NOT_PRESENT = 0x3a,
+ ASC_SAVING_PARAMETERS_NOT_SUPPORTED = 0x39,
+ ASC_LOGICAL_BLOCK_OOR = 0x21
+} asc_t;
+
+class LOWLEVEL_CDROM;
+
+class device_image_t
+{
+ public:
+ // Open a image. Returns non-negative if successful.
+ virtual int open (const char* pathname) = 0;
+
+ // Close the image.
+ virtual void close () = 0;
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ virtual off_t lseek (off_t offset, int whence) = 0;
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ virtual ssize_t read (void* buf, size_t count) = 0;
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ virtual ssize_t write (const void* buf, size_t count) = 0;
+
+ unsigned cylinders;
+ unsigned heads;
+ unsigned sectors;
+};
+
+// FLAT MODE
+class default_image_t : public device_image_t
+{
+ public:
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Open an image with specific flags. Returns non-negative if successful.
+ int open (const char* pathname, int flags);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ int fd;
+
+};
+
+// CONCAT MODE
+class concat_image_t : public device_image_t
+{
+ public:
+ // Default constructor
+ concat_image_t();
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+#define BX_CONCAT_MAX_IMAGES 8
+ int fd_table[BX_CONCAT_MAX_IMAGES];
+ off_t start_offset_table[BX_CONCAT_MAX_IMAGES];
+ off_t length_table[BX_CONCAT_MAX_IMAGES];
+ void increment_string (char *str);
+ int maxfd; // number of entries in tables that are valid
+
+ // notice if anyone does sequential read or write without seek in between.
+ // This can be supported pretty easily, but needs additional checks.
+ // 0=something other than seek was last operation
+ // 1=seek was last operation
+ int seek_was_last_op;
+
+ // the following variables tell which partial image file to use for
+ // the next read and write.
+ int index; // index into table
+ int fd; // fd to use for reads and writes
+ off_t thismin, thismax; // byte offset boundary of this image
+};
+
+// SPARSE MODE
+class sparse_image_t : public device_image_t
+{
+
+// Format of a sparse file:
+// 256 byte header, containing details such as page size and number of pages
+// Page indirection table, mapping virtual pages to physical pages within file
+// Physical pages till end of file
+
+ public:
+ // Default constructor
+ sparse_image_t();
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ int fd;
+
+#ifdef _POSIX_MAPPED_FILES
+ void * mmap_header;
+ size_t mmap_length;
+ size_t system_pagesize_mask;
+#endif
+ uint32 * pagetable;
+
+ // Header is written to disk in little-endian (x86) format
+ // Thus needs to be converted on big-endian systems before read
+ // The pagetable is also kept little endian
+
+ sparse_header_t header;
+
+ uint32 pagesize;
+ int pagesize_shift;
+ uint32 pagesize_mask;
+
+ off_t data_start;
+ off_t underlying_filesize;
+
+ char * pathname;
+
+ off_t position;
+
+ uint32 position_virtual_page;
+ uint32 position_physical_page;
+ uint32 position_page_offset;
+
+ off_t underlying_current_filepos;
+
+ off_t total_size;
+
+ void panic(const char * message);
+ off_t
+#ifndef PARANOID
+ sparse_image_t::
+#endif
+ get_physical_offset();
+ void
+#ifndef PARANOID
+ sparse_image_t::
+#endif
+ set_virtual_page(uint32 new_virtual_page);
+ void read_header();
+ ssize_t read_page_fragment(uint32 read_virtual_page, uint32 read_page_offset, size_t read_size, void * buf);
+
+ sparse_image_t * parent_image;
+};
+
+#if EXTERNAL_DISK_SIMULATOR
+#include "external-disk-simulator.h"
+#endif
+
+#if DLL_HD_SUPPORT
+class dll_image_t : public device_image_t
+{
+ public:
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ int vunit,vblk;
+
+};
+#endif
+
+// REDOLOG class
+class redolog_t
+{
+ public:
+ redolog_t();
+ int make_header (const char* type, Bit64u size);
+ int create (const char* filename, const char* type, Bit64u size);
+ int create (int filedes, const char* type, Bit64u size);
+ int open (const char* filename, const char* type, Bit64u size);
+ void close ();
+
+ off_t lseek (off_t offset, int whence);
+ ssize_t read (void* buf, size_t count);
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ void print_header();
+ int fd;
+ redolog_header_t header; // Header is kept in x86 (little) endianness
+ Bit32u *catalog;
+ Bit8u *bitmap;
+ Bit32u extent_index;
+ Bit32u extent_offset;
+ Bit32u extent_next;
+
+ Bit32u bitmap_blocs;
+ Bit32u extent_blocs;
+};
+
+// GROWING MODE
+class growing_image_t : public device_image_t
+{
+ public:
+ // Contructor
+ growing_image_t(Bit64u size);
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ redolog_t *redolog;
+ Bit64u size;
+};
+
+// UNDOABLE MODE
+class undoable_image_t : public device_image_t
+{
+ public:
+ // Contructor
+ undoable_image_t(Bit64u size, const char* redolog_name);
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ redolog_t *redolog; // Redolog instance
+ default_image_t *ro_disk; // Read-only flat disk instance
+ Bit64u size;
+ char *redolog_name; // Redolog name
+};
+
+
+// VOLATILE MODE
+class volatile_image_t : public device_image_t
+{
+ public:
+ // Contructor
+ volatile_image_t(Bit64u size, const char* redolog_name);
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ redolog_t *redolog; // Redolog instance
+ default_image_t *ro_disk; // Read-only flat disk instance
+ Bit64u size;
+ char *redolog_name; // Redolog name
+ char *redolog_temp; // Redolog temporary file name
+};
+
+
+#if BX_COMPRESSED_HD_SUPPORT
+
+#include <zlib.h>
+
+
+// Default compressed READ-ONLY image class
+class z_ro_image_t : public device_image_t
+{
+ public:
+ // Contructor
+ z_ro_image_t();
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ off_t offset;
+ int fd;
+ gzFile gzfile;
+
+};
+
+// Z-UNDOABLE MODE
+class z_undoable_image_t : public device_image_t
+{
+ public:
+ // Contructor
+ z_undoable_image_t(Bit64u size, const char* redolog_name);
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ redolog_t *redolog; // Redolog instance
+ z_ro_image_t *ro_disk; // Read-only compressed flat disk instance
+ Bit64u size;
+ char *redolog_name; // Redolog name
+};
+
+// Z-VOLATILE MODE
+class z_volatile_image_t : public device_image_t
+{
+ public:
+ // Contructor
+ z_volatile_image_t(Bit64u size, const char* redolog_name);
+
+ // Open a image. Returns non-negative if successful.
+ int open (const char* pathname);
+
+ // Close the image.
+ void close ();
+
+ // Position ourselves. Return the resulting offset from the
+ // beginning of the file.
+ off_t lseek (off_t offset, int whence);
+
+ // Read count bytes to the buffer buf. Return the number of
+ // bytes read (count).
+ ssize_t read (void* buf, size_t count);
+
+ // Write count bytes from buf. Return the number of bytes
+ // written (count).
+ ssize_t write (const void* buf, size_t count);
+
+ private:
+ redolog_t *redolog; // Redolog instance
+ z_ro_image_t *ro_disk; // Read-only compressed flat disk instance
+ Bit64u size;
+ char *redolog_name; // Redolog name
+ char *redolog_temp; // Redolog temporary file name
+};
+
+#endif
+
+
+typedef struct {
+ struct {
+ bx_bool busy;
+ bx_bool drive_ready;
+ bx_bool write_fault;
+ bx_bool seek_complete;
+ bx_bool drq;
+ bx_bool corrected_data;
+ bx_bool index_pulse;
+ unsigned index_pulse_count;
+ bx_bool err;
+ } status;
+ Bit8u error_register;
+ Bit8u head_no;
+ union {
+ Bit8u sector_count;
+ struct {
+#ifdef BX_LITTLE_ENDIAN
+ unsigned c_d : 1;
+ unsigned i_o : 1;
+ unsigned rel : 1;
+ unsigned tag : 5;
+#else /* BX_BIG_ENDIAN */
+ unsigned tag : 5;
+ unsigned rel : 1;
+ unsigned i_o : 1;
+ unsigned c_d : 1;
+#endif
+ } interrupt_reason;
+ };
+ Bit8u sector_no;
+ union {
+ Bit16u cylinder_no;
+ Bit16u byte_count;
+ };
+ Bit8u buffer[2048];
+ Bit32u buffer_index;
+ Bit32u drq_index;
+ Bit8u current_command;
+ Bit8u sectors_per_block;
+ Bit8u lba_mode;
+ struct {
+ bx_bool reset; // 0=normal, 1=reset controller
+ bx_bool disable_irq; // 0=allow irq, 1=disable irq
+ } control;
+ Bit8u reset_in_progress;
+ Bit8u features;
+ } controller_t;
+
+struct sense_info_t {
+ sense_t sense_key;
+ struct {
+ Bit8u arr[4];
+ } information;
+ struct {
+ Bit8u arr[4];
+ } specific_inf;
+ struct {
+ Bit8u arr[3];
+ } key_spec;
+ Bit8u fruc;
+ Bit8u asc;
+ Bit8u ascq;
+};
+
+struct error_recovery_t {
+ unsigned char data[8];
+
+ error_recovery_t ();
+};
+
+uint16 read_16bit(const uint8* buf) BX_CPP_AttrRegparmN(1);
+uint32 read_32bit(const uint8* buf) BX_CPP_AttrRegparmN(1);
+
+
+#ifdef LOWLEVEL_CDROM
+# include "cdrom.h"
+#endif
+
+
+struct cdrom_t
+{
+ bx_bool ready;
+ bx_bool locked;
+#ifdef LOWLEVEL_CDROM
+ LOWLEVEL_CDROM* cd;
+#endif
+ uint32 capacity;
+ int next_lba;
+ int remaining_blocks;
+ struct currentStruct {
+ error_recovery_t error_recovery;
+ } current;
+};
+
+struct atapi_t
+{
+ uint8 command;
+ int drq_bytes;
+ int total_bytes_remaining;
+};
+
+#if BX_USE_HD_SMF
+# define BX_HD_SMF static
+# define BX_HD_THIS theHardDrive->
+#else
+# define BX_HD_SMF
+# define BX_HD_THIS this->
+#endif
+
+typedef enum {
+ IDE_NONE, IDE_DISK, IDE_CDROM
+} device_type_t;
+
+class bx_hard_drive_c : public bx_hard_drive_stub_c {
+public:
+
+ bx_hard_drive_c(void);
+ virtual ~bx_hard_drive_c(void);
+ virtual void close_harddrive(void);
+ virtual void init();
+ virtual void reset(unsigned type);
+ virtual Bit32u get_device_handle(Bit8u channel, Bit8u device);
+ virtual Bit32u get_first_cd_handle(void);
+ virtual unsigned get_cd_media_status(Bit32u handle);
+ virtual unsigned set_cd_media_status(Bit32u handle, unsigned status);
+
+ virtual Bit32u virt_read_handler(Bit32u address, unsigned io_len) {
+ return read_handler (this, address, io_len);
+ }
+ virtual void virt_write_handler(Bit32u address,
+ Bit32u value, unsigned io_len)
+ {
+ write_handler(this, address, value, io_len);
+ }
+#if !BX_USE_HD_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+
+private:
+
+ BX_HD_SMF bx_bool calculate_logical_address(Bit8u channel, off_t *sector) BX_CPP_AttrRegparmN(2);
+ BX_HD_SMF void increment_address(Bit8u channel) BX_CPP_AttrRegparmN(1);
+ BX_HD_SMF void identify_drive(Bit8u channel);
+ BX_HD_SMF void identify_ATAPI_drive(Bit8u channel);
+ BX_HD_SMF void command_aborted(Bit8u channel, unsigned command);
+
+ BX_HD_SMF void init_send_atapi_command(Bit8u channel, Bit8u command, int req_length, int alloc_length, bool lazy = false) BX_CPP_AttrRegparmN(3);
+ BX_HD_SMF void ready_to_send_atapi(Bit8u channel) BX_CPP_AttrRegparmN(1);
+ BX_HD_SMF void raise_interrupt(Bit8u channel) BX_CPP_AttrRegparmN(1);
+ BX_HD_SMF void atapi_cmd_error(Bit8u channel, sense_t sense_key, asc_t asc);
+ BX_HD_SMF void init_mode_sense_single(Bit8u channel, const void* src, int size);
+ BX_HD_SMF void atapi_cmd_nop(Bit8u channel) BX_CPP_AttrRegparmN(1);
+
+ // FIXME:
+ // For each ATA channel we should have one controller struct
+ // and an array of two drive structs
+ struct channel_t {
+ struct drive_t {
+ device_image_t* hard_drive;
+ device_type_t device_type;
+ // 512 byte buffer for ID drive command
+ // These words are stored in native word endian format, as
+ // they are fetched and returned via a return(), so
+ // there's no need to keep them in x86 endian format.
+ Bit16u id_drive[256];
+
+ controller_t controller;
+ cdrom_t cdrom;
+ sense_info_t sense;
+ atapi_t atapi;
+
+ Bit8u model_no[41];
+ } drives[2];
+ unsigned drive_select;
+
+ Bit16u ioaddr1;
+ Bit16u ioaddr2;
+ Bit8u irq;
+
+ } channels[BX_MAX_ATA_CHANNEL];
+
+#if BX_PDC20230C_VLBIDE_SUPPORT
+// pdc20630c is only available for 1st ata channel
+ struct pdc20630c_t {
+ bx_bool prog_mode;
+ Bit8u prog_count;
+ Bit32u p1f3_value;
+ Bit32u p1f4_value;
+ } pdc20230c;
+#endif
+
+ };
+#endif // INCLUDE_ONLY_SPARSE_HEADER
+
diff --git a/tools/ioemu/iodev/ioapic.cc b/tools/ioemu/iodev/ioapic.cc
new file mode 100644
index 0000000000..8aaf67f848
--- /dev/null
+++ b/tools/ioemu/iodev/ioapic.cc
@@ -0,0 +1,175 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: ioapic.cc,v 1.11 2002/11/19 05:47:45 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#include <stdio.h>
+#include "bochs.h"
+#if BX_SUPPORT_APIC
+
+class bx_ioapic_c bx_ioapic;
+#define LOG_THIS bx_ioapic.
+
+void
+bx_io_redirect_entry_t::parse_value ()
+{
+ dest = (value >> 56) & 0xff;
+ masked = (value >> 16) & 1;
+ trig_mode = (value >> 15) & 1;
+ remote_irr = (value >> 14) & 1;
+ polarity = (value >> 13) & 1;
+ //delivery_status = (value >> 12) & 1;
+ delivery_status = 0; // always say the message has gone through
+ dest_mode = (value >> 11) & 1;
+ delivery_mode = (value >> 8) & 7;
+ vector = (value >> 0) & 0xff;
+}
+
+void
+bx_io_redirect_entry_t::sprintf_self (char *buf)
+{
+ sprintf (buf, "dest=%02x, masked=%d, trig_mode=%d, remote_irr=%d, polarity=%d, delivery_status=%d, dest_mode=%d, delivery_mode=%d, vector=%02x", dest, masked, trig_mode, remote_irr, polarity, delivery_status, dest_mode, delivery_mode, vector);
+}
+
+bx_ioapic_c::bx_ioapic_c ()
+ : bx_generic_apic_c ()
+{
+ put("IOAP");
+ settype(IOAPICLOG);
+}
+
+bx_ioapic_c::~bx_ioapic_c () {
+}
+
+void
+bx_ioapic_c::init ()
+{
+ bx_generic_apic_c::init ();
+ BX_DEBUG(("initializing I/O APIC"));
+ base_addr = 0xfec00000;
+ ioregsel = 0;
+ // all interrupts masked
+ for (int i=0; i<BX_IOAPIC_NUM_PINS; i++) {
+ ioredtbl[i].set_even_word (0x00010000);
+ ioredtbl[i].set_odd_word (0x00000000);
+ }
+ irr = 0;
+}
+
+void
+bx_ioapic_c::reset (unsigned type)
+{
+}
+
+void
+bx_ioapic_c::read_aligned(Bit32u address, Bit32u *data, unsigned len)
+{
+ BX_DEBUG( ("I/O APIC read_aligned addr=%08x, len=%d", address, len));
+ BX_ASSERT (len == 4);
+ address &= 0xff;
+ if (address == 0x00) {
+ // select register
+ *data = ioregsel;
+ return;
+ } else if (address != 0x10) {
+ BX_PANIC(("IOAPIC: read from unsupported address"));
+ }
+ // only reached when reading data register
+ switch (ioregsel) {
+ case 0x00: // APIC ID
+ *data = ((id & 0xf) << 24);
+ return;
+ case 0x01: // version
+ *data = (((BX_IOAPIC_NUM_PINS-1) & 0xff) << 16)
+ | (BX_IOAPIC_VERSION_ID & 0x0f);
+ return;
+ case 0x02:
+ BX_INFO(("IOAPIC: arbitration ID unsupported, returned 0"));
+ *data = 0;
+ return;
+ default:
+ int index = (ioregsel - 0x10) >> 1;
+ if (index >= 0 && index < BX_IOAPIC_NUM_PINS) {
+ bx_io_redirect_entry_t *entry = ioredtbl + index;
+ *data = (ioregsel&1) ? entry->get_odd_word() : entry->get_even_word ();
+ return;
+ }
+ BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel));
+ }
+}
+
+void
+bx_ioapic_c::write(Bit32u address, Bit32u *value, unsigned len)
+{
+ BX_DEBUG(("IOAPIC: write addr=%08x, data=%08x, len=%d", address, *value, len));
+ address &= 0xff;
+ if (address == 0x00) {
+ ioregsel = *value;
+ return;
+ } else if (address != 0x10) {
+ BX_PANIC(("IOAPIC: write to unsupported address"));
+ }
+ // only reached when writing data register
+ switch (ioregsel) {
+ case 0x00: // set APIC ID
+ {
+ Bit8u newid = (*value >> 24) & 0xf;
+ BX_INFO(("IOAPIC: setting id to 0x%x", newid));
+ set_id (newid);
+ return;
+ }
+ case 0x01: // version
+ case 0x02: // arbitration id
+ BX_INFO(("IOAPIC: could not write, IOREGSEL=0x%02x", ioregsel));
+ return;
+ default:
+ int index = (ioregsel - 0x10) >> 1;
+ if (index >= 0 && index < BX_IOAPIC_NUM_PINS) {
+ bx_io_redirect_entry_t *entry = ioredtbl + index;
+ if (ioregsel&1)
+ entry->set_odd_word (*value);
+ else
+ entry->set_even_word (*value);
+ char buf[1024];
+ entry->sprintf_self (buf);
+ BX_DEBUG(("IOAPIC: now entry[%d] is %s", index, buf));
+ service_ioapic ();
+ return;
+ }
+ BX_PANIC(("IOAPIC: IOREGSEL points to undefined register %02x", ioregsel));
+ }
+}
+
+void bx_ioapic_c::trigger_irq (unsigned vector, unsigned from)
+{
+ BX_DEBUG(("IOAPIC: received interrupt %d", vector));
+ if (vector >= 0 && vector < BX_IOAPIC_NUM_PINS) {
+ Bit32u bit = 1<<vector;
+ if ((irr & bit) == 0) {
+ irr |= bit;
+ service_ioapic ();
+ }
+ } else BX_PANIC(("IOAPIC: vector %d out of range", vector));
+}
+
+void bx_ioapic_c::untrigger_irq (unsigned num, unsigned from)
+{
+ BX_DEBUG(("IOAPIC: interrupt %d went away", num));
+}
+
+void bx_ioapic_c::service_ioapic ()
+{
+ // look in IRR and deliver any interrupts that are not masked.
+ BX_DEBUG(("IOAPIC: servicing"));
+ for (unsigned bit=0; bit < BX_IOAPIC_NUM_PINS; bit++) {
+ if (irr & (1<<bit)) {
+ bx_io_redirect_entry_t *entry = ioredtbl + bit;
+ if (!entry->masked) {
+ // clear irr bit and deliver
+ bx_bool done = deliver (entry->dest, entry->dest_mode, entry->delivery_mode, entry->vector, entry->polarity, entry->trig_mode);
+ if (done) irr &= ~(1<<bit);
+ }
+ }
+ }
+}
+
+#endif /* if BX_SUPPORT_APIC */
diff --git a/tools/ioemu/iodev/ioapic.h b/tools/ioemu/iodev/ioapic.h
new file mode 100644
index 0000000000..be008f04c8
--- /dev/null
+++ b/tools/ioemu/iodev/ioapic.h
@@ -0,0 +1,54 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: ioapic.h,v 1.5 2002/10/25 11:44:40 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+extern class bx_ioapic_c bx_ioapic;
+
+#define BX_IOAPIC_VERSION_ID 0x00170011 // same version as 82093 IOAPIC
+#define BX_IOAPIC_NUM_PINS 0x18
+
+class bx_io_redirect_entry_t {
+ Bit64u value;
+public:
+ Bit32u get_even_word () { return value & 0xffffffff; }
+ Bit32u get_odd_word () { return (value>>32) & 0xffffffff; }
+ void set_even_word (Bit32u even) {
+ // keep high 32 bits of value, replace low 32
+ value = ((value >> 32) << 32) | (even & 0xffffffff);
+ parse_value ();
+ }
+ void set_odd_word (Bit32u odd) {
+ // keep low 32 bits of value, replace high 32
+ value = (((Bit64u)odd & 0xffffffff) << 32) | (value & 0xffffffff);
+ parse_value ();
+ }
+ void parse_value ();
+ // parse_value sets the value and all the fields below. Do not change
+ // these fields except by calling parse_value.
+ Bit8u dest, masked, trig_mode, remote_irr, polarity, delivery_status, dest_mode, delivery_mode, vector;
+ void sprintf_self (char *buf);
+};
+
+class bx_ioapic_c : public bx_generic_apic_c {
+ Bit32u ioregsel; // selects between various registers
+ // interrupt request bitmask, not visible from the outside. Bits in the
+ // irr are set when trigger_irq is called, and cleared when the interrupt
+ // is delivered to the processor. If an interrupt is masked, the irr
+ // will still be set but delivery will not occur until it is unmasked.
+ // It's not clear if this is how the real device works.
+ Bit32u irr;
+public:
+ bx_io_redirect_entry_t ioredtbl[BX_IOAPIC_NUM_PINS]; // table of redirections
+ bx_ioapic_c ();
+ ~bx_ioapic_c ();
+ virtual void init ();
+ virtual void reset (unsigned type);
+ virtual void read_aligned(Bit32u address, Bit32u *data, unsigned len);
+ virtual void write(Bit32u address, Bit32u *value, unsigned len);
+ void trigger_irq (unsigned num, unsigned from);
+ void untrigger_irq (unsigned num, unsigned from);
+ void service_ioapic ();
+ virtual bx_bool match_logical_addr (Bit8u address) { return false; }
+ virtual bx_bool is_local_apic () { return false; }
+ virtual bx_apic_type_t get_type () { return APIC_TYPE_IOAPIC; }
+};
diff --git a/tools/ioemu/iodev/iodebug.cc b/tools/ioemu/iodev/iodebug.cc
new file mode 100644
index 0000000000..ca2314ede6
--- /dev/null
+++ b/tools/ioemu/iodev/iodebug.cc
@@ -0,0 +1,354 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: iodebug.cc,v 1.15 2002/11/19 05:47:45 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#include "bochs.h"
+#if BX_IODEBUG_SUPPORT
+
+
+
+bx_iodebug_c bx_iodebug;
+bx_iodebug_c *bx_iodebug_ptr;
+
+ struct bx_iodebug_s_type {
+ bx_bool enabled;
+ unsigned int register_select;
+ Bit32u registers[2];
+ Bit32u monitored_mem_areas_start[BX_IODEBUG_MAX_AREAS];
+ Bit32u monitored_mem_areas_end[BX_IODEBUG_MAX_AREAS];
+ } bx_iodebug_s;
+
+
+
+
+// Constructor
+bx_iodebug_c::bx_iodebug_c( void )
+{
+ put("IODEBUG");
+ settype(IODEBUGLOG);
+
+}
+
+
+
+
+
+// Destructor
+bx_iodebug_c::~bx_iodebug_c( void )
+{
+}
+
+
+
+
+
+void bx_iodebug_c::init(void)
+{
+ int i;
+
+ DEV_register_ioread_handler(this, read_handler, 0x8A00,"BOCHS IODEBUG", 7);
+ DEV_register_iowrite_handler(this, write_handler, 0x8A00,"BOCHS IODEBUG", 7);
+ DEV_register_iowrite_handler(this, write_handler, 0x8A01,"BOCHS IODEBUG", 7);
+// fprintf( stderr, "IODEBUG initialized\n");
+
+ bx_iodebug_s.enabled = 0;
+ bx_iodebug_s.register_select = 0;
+ for(i=0;i<BX_IODEBUG_MAX_AREAS;i++) {
+ bx_iodebug_s.monitored_mem_areas_start[i] = 0;
+ bx_iodebug_s.monitored_mem_areas_end[i] = 0;
+ }
+}
+
+void bx_iodebug_c::reset(unsigned type)
+{
+}
+
+
+Bit32u bx_iodebug_c::read_handler(void *this_ptr, Bit32u addr, unsigned io_len)
+{
+ bx_iodebug_ptr = (bx_iodebug_c *) this_ptr;
+ return( bx_iodebug_ptr->read(addr, io_len) );
+}
+
+
+
+
+
+
+Bit32u bx_iodebug_c::read( Bit32u addr, unsigned io_len )
+{
+
+ if(bx_iodebug_s.enabled) return(0x8A00);
+ return(0);
+}
+
+
+
+
+
+
+
+
+
+
+void bx_iodebug_c::write_handler(void *this_ptr, Bit32u addr, Bit32u dvalue, unsigned io_len)
+{
+ bx_iodebug_c *class_ptr = (bx_iodebug_c *) this_ptr;
+ class_ptr->write( addr, dvalue, io_len );
+}
+
+
+
+
+
+
+void bx_iodebug_c::write( Bit32u addr, Bit32u dvalue, unsigned int io_len )
+{
+
+
+// fprintf(stderr, "IODEBUG addr: %4x\tdvalue: %8x\tio_len: %8x\n", (unsigned int)addr, (unsigned int)dvalue, io_len);
+
+ if( addr == 0x8A01 && io_len == 2 )
+ {
+ bx_iodebug_s.registers[bx_iodebug_s.register_select] =
+ (bx_iodebug_s.registers[bx_iodebug_s.register_select] << 16) +
+ (dvalue & 0x0000FFFF );
+ }
+
+ if( (addr != 0x8A00) || (io_len != 2) ) return;
+
+ if( !bx_iodebug_s.enabled )
+ {
+ if( dvalue == 0x8A00 )
+ {
+ bx_iodebug_s.enabled = 1;
+// fprintf(stderr, "IODEBUG enabled\n");
+ bx_iodebug_s.registers[0] = 0;
+ bx_iodebug_s.registers[1] = 0;
+ }
+ return;
+ }
+
+ switch( dvalue )
+ {
+ case( 0x8A01 ):
+ bx_iodebug_s.register_select = 0;
+// fprintf( stderr, "IODEBUG register 0 selected\n");
+ break;
+
+ case( 0x8A02 ):
+ bx_iodebug_s.register_select = 1;
+// fprintf( stderr, "IODEBUG register 1 selected\n");
+ break;
+
+ case( 0x8A80 ):
+ bx_iodebug_s.register_select = 0;
+ bx_iodebug_c::add_range(
+ bx_iodebug_s.registers[0],
+ bx_iodebug_s.registers[1]);
+ bx_iodebug_s.registers[0] = 0;
+ bx_iodebug_s.registers[1] = 0;
+ break;
+
+#if BX_DEBUGGER
+ case( 0x8AE0 ):
+ fprintf( stderr, "request return to dbg prompt received, 0x8AE0 command (iodebug)\n");
+ bx_guard.interrupt_requested=1;
+ break;
+
+ case( 0x8AE2):
+ fprintf( stderr, "request made by the guest os to disable tracing, iodebug port 0x8A00->0x8AE2\n");
+ BX_CPU(dbg_cpu)->trace = 0;
+ break;
+
+ case( 0x8AE3 ):
+ fprintf( stderr, "request made by the guest os to enable tracing, iodebug port 0x8A00->0x8AE3\n");
+ BX_CPU(dbg_cpu)->trace = 1;
+ break;
+
+ case( 0x8AE4 ):
+ fprintf( stderr, "request made by the guest os to disable register tracing, iodebug port 0x8A00->0x8AE4\n");
+ BX_CPU(dbg_cpu)->trace_reg = 0;
+ break;
+
+ case( 0x8AE5 ):
+ fprintf( stderr, "request made by the guest os to enable register tracing, iodebug port 0x8A00->0x8AE5\n");
+ BX_CPU(dbg_cpu)->trace_reg = 1;
+ break;
+
+#endif
+
+ case( 0x8AFF ):
+ bx_iodebug_s.enabled = 0;
+// fprintf( stderr, "IODEBUG device deactivated\n");
+// break;
+
+// default:
+// fprintf(stderr,"IODEBUG unsupported register code\n");
+ }
+}
+
+
+
+
+
+
+
+
+// Static function
+void bx_iodebug_c::mem_write( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data)
+{
+ Bit32u data32;
+ Bit16u data16;
+ Bit8u data8;
+
+ unsigned int area;
+ if( !bx_iodebug_s.enabled ) return;
+
+ area = bx_iodebug_c::range_test( addr, len );
+ // Device is enabled, testing address ranges
+ if( area )
+ {
+ area--;
+#if BX_DEBUGGER
+ fprintf( stdout, "%s @ eip: %08X wrote at monitored memory location %8X\n", cpu->name, cpu->get_EIP(), addr);
+ bx_guard.interrupt_requested=1;
+#else
+ fprintf( stderr,
+ "IODEBUG write to monitored memory area: %2i\tby EIP:\t\t%08X\n\trange start: \t\t%08X\trange end:\t%08X\n\taddress accessed:\t%08X\tdata written:\t",
+ area,
+ cpu->get_EIP(),
+ bx_iodebug_s.monitored_mem_areas_start[area],
+ bx_iodebug_s.monitored_mem_areas_end[area],
+ (unsigned int)addr);
+
+ data32 = * (Bit32u *)data;
+ data16 = (Bit16u)data32;
+ data8 = (Bit8u)data32;
+
+ switch(len)
+ {
+ case(1):
+ fprintf(stderr,"%02X\n", (unsigned int)data8);
+ break;
+
+ case(2):
+ fprintf(stderr,"%04X\n", (unsigned int)data16);
+ break;
+
+ case(4):
+ fprintf(stderr,"%08X\n", (unsigned int)data32);
+ break;
+
+ default:
+ fprintf(stderr, "unsupported write size\n");
+ }
+#endif
+ }
+}
+
+
+
+
+
+
+
+
+void bx_iodebug_c::mem_read( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data)
+{
+ Bit32u data32;
+ Bit16u data16;
+ Bit8u data8;
+
+ unsigned int area;
+ if( !bx_iodebug_s.enabled ) return;
+
+ area = bx_iodebug_c::range_test( addr, len );
+ // Device is enabled, testing address ranges
+ if( area )
+ {
+ area--;
+#if BX_DEBUGGER
+ fprintf( stdout, "%s @ eip: %8X wrote at monitored memory location %8X\n", cpu->name, cpu->get_EIP(), addr);
+ bx_guard.interrupt_requested=1;
+#else
+ fprintf( stderr,
+ "IODEBUG read to monitored memory area: %2i\tby EIP:\t\t%08X\n\trange start: \t\t%08X\trange end:\t%08X\n\taddress accessed:\t%08X\tdata written:\t",
+ area,
+ cpu->get_EIP(),
+ bx_iodebug_s.monitored_mem_areas_start[area],
+ bx_iodebug_s.monitored_mem_areas_end[area],
+ (unsigned int)addr);
+ data32 = * (Bit32u *)data;
+ data16 = (Bit16u)data32;
+ data8 = (Bit8u)data32;
+
+ switch(len)
+ {
+ case(1):
+ fprintf(stderr,"%02X\n", (unsigned int)data8);
+ break;
+
+ case(2):
+ fprintf(stderr,"%04X\n", (unsigned int)data16);
+ break;
+
+ case(4):
+ fprintf(stderr,"%08X\n", (unsigned int)data32);
+ break;
+
+ default:
+ fprintf(stderr, "unsupported write size\n");
+ }
+#endif
+ }
+}
+
+
+
+
+
+
+
+unsigned int bx_iodebug_c::range_test( Bit32u addr, unsigned int len )
+{
+ unsigned int i;
+
+ for(i=0;i<BX_IODEBUG_MAX_AREAS;i++)
+ {
+ if( (bx_iodebug_s.monitored_mem_areas_start[i]!=0) ||
+ (bx_iodebug_s.monitored_mem_areas_end[i]!=0) )
+ {
+ if( (Bit32u)(addr+len-1) < bx_iodebug_s.monitored_mem_areas_start[i] )
+ continue;
+ if( addr < bx_iodebug_s.monitored_mem_areas_end[i] )
+ {
+ return(++i);
+ }
+ }
+ }
+ return(0);
+}
+
+
+
+
+
+
+void bx_iodebug_c::add_range( Bit32u addr_start, Bit32u addr_end )
+{
+ unsigned int i;
+ for(i=0;i<BX_IODEBUG_MAX_AREAS;i++)
+ {
+ if( !bx_iodebug_s.monitored_mem_areas_start[i] &&
+ !bx_iodebug_s.monitored_mem_areas_end[i] )
+ {
+ bx_iodebug_s.monitored_mem_areas_start[i] = addr_start;
+ bx_iodebug_s.monitored_mem_areas_end[i] = addr_end;
+// fprintf(stderr, "IODEBUG added range successfully in slot: %i\n",i);
+ return;
+ }
+ }
+// fprintf(stderr, "IODEBUG unable to register memory range, all slots taken\n");
+}
+#endif /* if BX_IODEBUG_SUPPORT */
diff --git a/tools/ioemu/iodev/iodebug.h b/tools/ioemu/iodev/iodebug.h
new file mode 100644
index 0000000000..a31f7cfa92
--- /dev/null
+++ b/tools/ioemu/iodev/iodebug.h
@@ -0,0 +1,35 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: iodebug.h,v 1.7 2002/10/26 03:53:22 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#ifndef _BX_IODEBUG_H
+#define _BX_IODEBUG_H
+
+#include "config.h"
+
+#define BX_IODEBUG_THIS this->
+
+#define BX_IODEBUG_MAX_AREAS 30
+
+class bx_iodebug_c : public bx_devmodel_c
+{
+public:
+ bx_iodebug_c( void );
+ ~bx_iodebug_c( void );
+ virtual void init(void);
+ virtual void reset (unsigned type);
+ static void mem_write( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data);
+ static void mem_read( BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data);
+
+private:
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+ Bit32u read(Bit32u addr, unsigned int io_len);
+ void write(Bit32u addr, Bit32u dvalue, unsigned int io_len);
+ static unsigned int range_test(Bit32u addr, unsigned int len);
+ static void add_range( Bit32u addr_start, Bit32u addr_end);
+
+};
+
+extern bx_iodebug_c bx_iodebug;
+#endif
diff --git a/tools/ioemu/iodev/iodev.h b/tools/ioemu/iodev/iodev.h
new file mode 100644
index 0000000000..3057f6c334
--- /dev/null
+++ b/tools/ioemu/iodev/iodev.h
@@ -0,0 +1,422 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: iodev.h,v 1.37 2003/08/04 16:03:09 akrisak Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+/* maximum number of emulated devices allowed. floppy, vga, etc...
+ you can increase this to anything below 256 since an 8-bit handle
+ is used for each device */
+#define BX_MAX_IO_DEVICES 30
+
+/* the last device in the array is the "default" I/O device */
+#define BX_DEFAULT_IO_DEVICE (BX_MAX_IO_DEVICES-1)
+
+/* number of IRQ lines supported. In an ISA PC there are two
+ PIC chips cascaded together. each has 8 IRQ lines, so there
+ should be 16 IRQ's total */
+#define BX_MAX_IRQS 16
+#define BX_NO_IRQ -1
+
+
+class bx_pit_c;
+class bx_keyb_c;
+class bx_ioapic_c;
+class bx_g2h_c;
+#if BX_IODEBUG_SUPPORT
+class bx_iodebug_c;
+#endif
+
+
+
+typedef Bit32u (*bx_read_handler_t)(void *, Bit32u, unsigned);
+typedef void (*bx_write_handler_t)(void *, Bit32u, Bit32u, unsigned);
+
+
+#if BX_USE_DEV_SMF
+# define BX_DEV_SMF static
+# define BX_DEV_THIS bx_devices.
+#else
+# define BX_DEV_SMF
+# define BX_DEV_THIS this->
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// bx_devmodel_c declaration
+//////////////////////////////////////////////////////////////////////
+
+// This class defines virtual methods that are common to all devices.
+// Child classes do not need to implement all of them, because in this
+// definition they are defined as empty, as opposed to being pure
+// virtual (= 0).
+class BOCHSAPI bx_devmodel_c : public logfunctions {
+ public:
+ virtual ~bx_devmodel_c () {}
+ virtual void init_mem(BX_MEM_C *) {}
+ virtual void init(void) {}
+ virtual void reset(unsigned type) {}
+ virtual void device_load_state () {}
+ virtual void device_save_state () {}
+};
+
+//////////////////////////////////////////////////////////////////////
+// declare stubs for devices
+//////////////////////////////////////////////////////////////////////
+
+#define STUBFUNC(dev,method) \
+ pluginlog->panic("%s called in %s stub. you must not have loaded the %s plugin", #dev, #method, #dev )
+
+class BOCHSAPI bx_keyb_stub_c : public bx_devmodel_c {
+ public:
+ virtual ~bx_keyb_stub_c () {}
+ // stubs for bx_keyb_c methods
+ virtual void mouse_motion(int delta_x, int delta_y, unsigned button_state) {
+ STUBFUNC(keyboard, mouse_motion);
+ }
+ virtual void gen_scancode(Bit32u key) {
+ STUBFUNC(keyboard, gen_scancode);
+ }
+ virtual void paste_bytes(Bit8u *data, Bit32s length) {
+ STUBFUNC(keyboard, paste_bytes);
+ }
+ virtual void paste_delay_changed () {
+ STUBFUNC(keyboard, paste_delay_changed);
+ }
+ virtual void mouse_enabled_changed(bool enabled) {
+ STUBFUNC(keyboard, mouse_enabled_changed);
+ }
+};
+
+class BOCHSAPI bx_hard_drive_stub_c : public bx_devmodel_c {
+ public:
+ virtual void close_harddrive(void) {
+ STUBFUNC(HD, close_harddrive);
+ }
+ virtual void init() {
+ STUBFUNC(HD, init);
+ }
+ virtual void reset(unsigned type) {
+ STUBFUNC(HD, reset);
+ }
+ virtual Bit32u get_device_handle(Bit8u channel, Bit8u device) {
+ STUBFUNC(HD, get_device_handle); return 0;
+ }
+ virtual Bit32u get_first_cd_handle(void) {
+ STUBFUNC(HD, get_first_cd_handle); return 0;
+ }
+ virtual unsigned get_cd_media_status(Bit32u handle) {
+ STUBFUNC(HD, get_cd_media_status); return 0;
+ }
+ virtual unsigned set_cd_media_status(Bit32u handle, unsigned status) {
+ STUBFUNC(HD, set_cd_media_status); return 0;
+ }
+ virtual Bit32u virt_read_handler(Bit32u address, unsigned io_len)
+ {
+ STUBFUNC(HD, virt_read_handler); return 0;
+ }
+ virtual void virt_write_handler(Bit32u address,
+ Bit32u value, unsigned io_len)
+ {
+ STUBFUNC(HD, virt_write_handler);
+ }
+};
+
+class BOCHSAPI bx_floppy_stub_c : public bx_devmodel_c {
+ public:
+ virtual unsigned get_media_status(unsigned drive) {
+ STUBFUNC(floppy, get_media_status); return 0;
+ }
+ virtual unsigned set_media_status(unsigned drive, unsigned status) {
+ STUBFUNC(floppy, set_media_status); return 0;
+ }
+};
+
+class BOCHSAPI bx_cmos_stub_c : public bx_devmodel_c {
+ public:
+ virtual Bit32u get_reg(unsigned reg) {
+ STUBFUNC(cmos, get_reg); return 0;
+ }
+ virtual void set_reg(unsigned reg, Bit32u val) {
+ STUBFUNC(cmos, set_reg);
+ }
+ virtual time_t get_timeval() {
+ // STUBFUNC(cmos, get_timeval);
+ return 0;
+ }
+ virtual void checksum_cmos(void) {
+ STUBFUNC(cmos, checksum);
+ }
+};
+
+class BOCHSAPI bx_dma_stub_c : public bx_devmodel_c {
+ public:
+ virtual unsigned registerDMA8Channel(
+ unsigned channel,
+ void (* dmaRead)(Bit8u *data_byte),
+ void (* dmaWrite)(Bit8u *data_byte),
+ const char *name
+ ) {
+ STUBFUNC(dma, registerDMA8Channel); return 0;
+ }
+ virtual unsigned registerDMA16Channel(
+ unsigned channel,
+ void (* dmaRead)(Bit16u *data_word),
+ void (* dmaWrite)(Bit16u *data_word),
+ const char *name
+ ) {
+ STUBFUNC(dma, registerDMA16Channel); return 0;
+ }
+ virtual unsigned unregisterDMAChannel(unsigned channel) {
+ STUBFUNC(dma, unregisterDMAChannel); return 0;
+ }
+ virtual unsigned get_TC(void) {
+ STUBFUNC(dma, get_TC); return 0;
+ }
+ virtual void set_DRQ(unsigned channel, bx_bool val) {
+ STUBFUNC(dma, set_DRQ);
+ }
+ virtual void raise_HLDA(void) {
+ STUBFUNC(dma, raise_HLDA);
+ }
+};
+
+class BOCHSAPI bx_pic_stub_c : public bx_devmodel_c {
+ public:
+ virtual void raise_irq(unsigned irq_no) {
+ STUBFUNC(pic, raise_irq);
+ }
+ virtual void lower_irq(unsigned irq_no) {
+ STUBFUNC(pic, lower_irq);
+ }
+ virtual Bit8u IAC(void) {
+ STUBFUNC(pic, IAC); return 0;
+ }
+ virtual void show_pic_state(void) {
+ STUBFUNC(pic, show_pic_state);
+ }
+};
+
+class BOCHSAPI bx_vga_stub_c : public bx_devmodel_c {
+ public:
+ virtual void redraw_area(unsigned x0, unsigned y0,
+ unsigned width, unsigned height) {
+ STUBFUNC(vga, redraw_area);
+ }
+ virtual Bit8u mem_read(Bit32u addr) {
+ STUBFUNC(vga, mem_read); return 0;
+ }
+ virtual void mem_write(Bit32u addr, Bit8u value) {
+ STUBFUNC(vga, mem_write);
+ }
+ virtual void get_text_snapshot(Bit8u **text_snapshot,
+ unsigned *txHeight, unsigned *txWidth) {
+ STUBFUNC(vga, get_text_snapshot);
+ }
+ virtual void trigger_timer(void *this_ptr) {
+ STUBFUNC(vga, trigger_timer);
+ }
+ virtual void set_update_interval (unsigned interval) {
+ STUBFUNC(vga, set_update_interval);
+ }
+ virtual Bit8u get_actl_palette_idx(Bit8u index) {
+ return 0;
+ }
+};
+
+class BOCHSAPI bx_pci_stub_c : public bx_devmodel_c {
+ public:
+ virtual bx_bool register_pci_handlers(void *this_ptr,
+ Bit32u (*bx_pci_read_handler)(void *, Bit8u, unsigned),
+ void(*bx_pci_write_handler)(void *, Bit8u, Bit32u, unsigned),
+ Bit8u devfunc, const char *name) {
+ STUBFUNC(pci, register_pci_handlers); return 0;
+ }
+ virtual Bit8u rd_memType (Bit32u addr) {
+ return 0;
+ }
+ virtual Bit8u wr_memType (Bit32u addr) {
+ return 0;
+ }
+ virtual void print_i440fx_state(void) {}
+};
+
+class BOCHSAPI bx_ne2k_stub_c : public bx_devmodel_c {
+ public:
+ virtual void print_info(FILE *file, int page, int reg, int nodups) {}
+};
+
+class BOCHSAPI bx_devices_c : public logfunctions {
+public:
+ bx_devices_c(void);
+ ~bx_devices_c(void);
+ // Register I/O addresses and IRQ lines. Initialize any internal
+ // structures. init() is called only once, even if the simulator
+ // reboots or is restarted.
+ void init(BX_MEM_C *);
+ // Enter reset state in response to a reset condition.
+ // The types of reset conditions are defined in bochs.h:
+ // power-on, hardware, or software.
+ void reset(unsigned type);
+ BX_MEM_C *mem; // address space associated with these devices
+ bx_bool register_io_read_handler(void *this_ptr, bx_read_handler_t f, Bit32u addr, const char *name, Bit8u mask );
+ bx_bool register_io_write_handler(void *this_ptr, bx_write_handler_t f, Bit32u addr, const char *name, Bit8u mask );
+ bx_bool register_default_io_read_handler(void *this_ptr, bx_read_handler_t f, const char *name, Bit8u mask );
+ bx_bool register_default_io_write_handler(void *this_ptr, bx_write_handler_t f, const char *name, Bit8u mask );
+ bx_bool register_irq(unsigned irq, const char *name);
+ bx_bool unregister_irq(unsigned irq, const char *name);
+ void iodev_init(void);
+ Bit32u inp(Bit16u addr, unsigned io_len) BX_CPP_AttrRegparmN(2);
+ void outp(Bit16u addr, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3);
+
+ static void timer_handler(void *);
+ void timer(void);
+
+ bx_devmodel_c *pluginBiosDevice;
+ bx_ioapic_c *ioapic;
+ bx_pci_stub_c *pluginPciBridge;
+ bx_devmodel_c *pluginPci2IsaBridge;
+ bx_devmodel_c *pluginPciVgaAdapter;
+ bx_devmodel_c *pluginPciUSBAdapter;
+ bx_pit_c *pit;
+ bx_keyb_stub_c *pluginKeyboard;
+ bx_dma_stub_c *pluginDmaDevice;
+ bx_floppy_stub_c *pluginFloppyDevice;
+ bx_cmos_stub_c *pluginCmosDevice;
+ bx_devmodel_c *pluginSerialDevice;
+ bx_devmodel_c *pluginParallelDevice;
+ bx_devmodel_c *pluginUnmapped;
+ bx_vga_stub_c *pluginVgaDevice;
+ bx_pic_stub_c *pluginPicDevice;
+ bx_hard_drive_stub_c *pluginHardDrive;
+ bx_devmodel_c *pluginSB16Device;
+ bx_ne2k_stub_c *pluginNE2kDevice;
+ bx_g2h_c *g2h;
+ bx_devmodel_c *pluginExtFpuIrq;
+ bx_devmodel_c *pluginGameport;
+#if BX_IODEBUG_SUPPORT
+ bx_iodebug_c *iodebug;
+#endif
+
+ // stub classes that the pointers (above) can point to until a plugin is
+ // loaded
+ bx_cmos_stub_c stubCmos;
+ bx_keyb_stub_c stubKeyboard;
+ bx_hard_drive_stub_c stubHardDrive;
+ bx_dma_stub_c stubDma;
+ bx_pic_stub_c stubPic;
+ bx_floppy_stub_c stubFloppy;
+ bx_vga_stub_c stubVga;
+ bx_pci_stub_c stubPci;
+ bx_ne2k_stub_c stubNE2k;
+
+ // Some info to pass to devices which can handled bulk IO. This allows
+ // the interface to remain the same for IO devices which can't handle
+ // bulk IO. We should probably implement special INPBulk() and OUTBulk()
+ // functions which stick these values in the bx_devices_c class, and
+ // then call the normal functions rather than having gross globals
+ // variables.
+ Bit32u bulkIOHostAddr;
+ unsigned bulkIOQuantumsRequested;
+ unsigned bulkIOQuantumsTransferred;
+
+private:
+
+ Bit8u read_handler_id[0x10000]; // 64K
+ struct {
+ bx_read_handler_t funct;
+ void *this_ptr;
+ const char *handler_name; // name of device
+ Bit8u mask; // io_len mask
+ } io_read_handler[BX_MAX_IO_DEVICES];
+ unsigned num_read_handles;
+
+ Bit8u write_handler_id[0x10000]; // 64K
+ struct {
+ bx_write_handler_t funct;
+ void *this_ptr;
+ const char *handler_name; // name of device
+ Bit8u mask; // io_len mask
+ } io_write_handler[BX_MAX_IO_DEVICES];
+ unsigned num_write_handles;
+
+ // more for informative purposes, the names of the devices which
+ // are use each of the IRQ 0..15 lines are stored here
+ const char *irq_handler_name[BX_MAX_IRQS];
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+ BX_DEV_SMF Bit32u port92_read(Bit32u address, unsigned io_len);
+ BX_DEV_SMF void port92_write(Bit32u address, Bit32u value, unsigned io_len);
+
+ static Bit32u default_read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void default_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+
+ int timer_handle;
+ bx_bool is_serial_enabled ();
+ bx_bool is_usb_enabled ();
+ bx_bool is_parallel_enabled ();
+ };
+
+
+
+#if BX_PCI_SUPPORT
+#include "iodev/pci.h"
+#include "iodev/pci2isa.h"
+#if BX_PCI_VGA_SUPPORT
+#include "iodev/pcivga.h"
+#endif
+#if BX_PCI_USB_SUPPORT
+#include "iodev/pciusb.h"
+#endif
+#endif
+#include "iodev/vga.h"
+#if BX_SUPPORT_APIC
+# include "iodev/ioapic.h"
+#endif
+#include "iodev/biosdev.h"
+#include "iodev/cmos.h"
+#include "iodev/dma.h"
+#include "iodev/floppy.h"
+#include "iodev/harddrv.h"
+#if BX_IODEBUG_SUPPORT
+# include "iodev/iodebug.h"
+#endif
+#include "iodev/keyboard.h"
+#include "iodev/parallel.h"
+#include "iodev/pic.h"
+#include "iodev/pit.h"
+#include "iodev/pit_wrap.h"
+#include "iodev/virt_timer.h"
+#include "iodev/serial.h"
+#if BX_SUPPORT_SB16
+# include "iodev/sb16.h"
+#endif
+#include "iodev/unmapped.h"
+#include "iodev/eth.h"
+#include "iodev/ne2k.h"
+#include "iodev/guest2host.h"
+#include "iodev/slowdown_timer.h"
+#include "iodev/extfpuirq.h"
+#include "iodev/gameport.h"
diff --git a/tools/ioemu/iodev/keyboard.cc b/tools/ioemu/iodev/keyboard.cc
new file mode 100644
index 0000000000..693f4a4c60
--- /dev/null
+++ b/tools/ioemu/iodev/keyboard.cc
@@ -0,0 +1,1611 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: keyboard.cc,v 1.82 2003/11/11 18:18:36 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Now features proper implementation of keyboard opcodes 0xF4 to 0xF6
+// Silently ignores PS/2 keyboard extensions (0xF7 to 0xFD)
+// Explicit panic on resend (0xFE)
+//
+// Emmanuel Marty <core@ggi-project.org>
+
+// NB: now the PS/2 mouse support is in, outb changes meaning
+// in conjunction with auxb
+// auxb == 0 && outb == 0 => both buffers empty (nothing to read)
+// auxb == 0 && outb == 1 => keyboard controller output buffer full
+// auxb == 1 && outb == 0 => not used
+// auxb == 1 && outb == 1 => mouse output buffer full.
+// (das)
+
+// Notes from Christophe Bothamy <cbbochs@free.fr>
+//
+// This file includes code from Ludovic Lange (http://ludovic.lange.free.fr)
+// Implementation of 3 scancodes sets mf1,mf2,mf3 with or without translation.
+// Default is mf2 with translation
+// Ability to switch between scancodes sets
+// Ability to turn translation on or off
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#include <math.h>
+#include "scancodes.h"
+
+#define LOG_THIS theKeyboard->
+#define VERBOSE_KBD_DEBUG 0
+
+
+bx_keyb_c *theKeyboard = NULL;
+
+ int
+libkeyboard_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ // Create one instance of the keyboard device object.
+ theKeyboard = new bx_keyb_c ();
+ // Before this plugin was loaded, pluginKeyboard pointed to a stub.
+ // Now make it point to the real thing.
+ bx_devices.pluginKeyboard = theKeyboard;
+ // Register this device.
+ BX_REGISTER_DEVICE_DEVMODEL (plugin, type, theKeyboard, BX_PLUGIN_KEYBOARD);
+ return(0); // Success
+}
+
+ void
+libkeyboard_LTX_plugin_fini(void)
+{
+ BX_INFO (("keyboard plugin_fini"));
+}
+
+bx_keyb_c::bx_keyb_c(void)
+{
+ // constructor
+ put("KBD");
+ settype(KBDLOG);
+}
+
+bx_keyb_c::~bx_keyb_c(void)
+{
+ // destructor
+ BX_DEBUG(("Exit."));
+}
+
+
+// flush internal buffer and reset keyboard settings to power-up condition
+ void
+bx_keyb_c::resetinternals(bx_bool powerup)
+{
+ Bit32u i;
+
+ BX_KEY_THIS s.kbd_internal_buffer.num_elements = 0;
+ for (i=0; i<BX_KBD_ELEMENTS; i++)
+ BX_KEY_THIS s.kbd_internal_buffer.buffer[i] = 0;
+ BX_KEY_THIS s.kbd_internal_buffer.head = 0;
+
+ BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 0;
+
+ // Default scancode set is mf2 with translation
+ BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 0;
+ BX_KEY_THIS s.kbd_controller.current_scancodes_set = 1;
+ BX_KEY_THIS s.kbd_controller.scancodes_translate = 1;
+
+ if (powerup) {
+ BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 0;
+ BX_KEY_THIS s.kbd_internal_buffer.delay = 1; // 500 mS
+ BX_KEY_THIS s.kbd_internal_buffer.repeat_rate = 0x0b; // 10.9 chars/sec
+ }
+}
+
+
+
+ void
+bx_keyb_c::init(void)
+{
+ BX_DEBUG(("Init $Id: keyboard.cc,v 1.82 2003/11/11 18:18:36 vruppert Exp $"));
+ Bit32u i;
+
+ DEV_register_irq(1, "8042 Keyboard controller");
+ DEV_register_irq(12, "8042 Keyboard controller (PS/2 mouse)");
+
+ DEV_register_ioread_handler(this, read_handler,
+ 0x0060, "8042 Keyboard controller", 1);
+ DEV_register_ioread_handler(this, read_handler,
+ 0x0064, "8042 Keyboard controller", 1);
+ DEV_register_iowrite_handler(this, write_handler,
+ 0x0060, "8042 Keyboard controller", 1);
+ DEV_register_iowrite_handler(this, write_handler,
+ 0x0064, "8042 Keyboard controller", 1);
+ BX_KEY_THIS timer_handle = bx_pc_system.register_timer( this, timer_handler,
+ bx_options.Okeyboard_serial_delay->get(), 1, 1,
+ "8042 Keyboard controller");
+
+ resetinternals(1);
+
+ BX_KEY_THIS s.kbd_internal_buffer.led_status = 0;
+ BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1;
+
+ BX_KEY_THIS s.mouse_internal_buffer.num_elements = 0;
+ for (i=0; i<BX_MOUSE_BUFF_SIZE; i++)
+ BX_KEY_THIS s.mouse_internal_buffer.buffer[i] = 0;
+ BX_KEY_THIS s.mouse_internal_buffer.head = 0;
+
+ // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.pare = 0;
+ BX_KEY_THIS s.kbd_controller.tim = 0;
+ BX_KEY_THIS s.kbd_controller.auxb = 0;
+ BX_KEY_THIS s.kbd_controller.keyl = 1;
+ BX_KEY_THIS s.kbd_controller.c_d = 1;
+ BX_KEY_THIS s.kbd_controller.sysf = 0;
+ BX_KEY_THIS s.kbd_controller.inpb = 0;
+ BX_KEY_THIS s.kbd_controller.outb = 0;
+
+ BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 1;
+ BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 0;
+ BX_KEY_THIS s.kbd_controller.allow_irq1 = 1;
+ BX_KEY_THIS s.kbd_controller.allow_irq12 = 1;
+ BX_KEY_THIS s.kbd_controller.kbd_output_buffer = 0;
+ BX_KEY_THIS s.kbd_controller.aux_output_buffer = 0;
+ BX_KEY_THIS s.kbd_controller.last_comm = 0;
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 0;
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 0;
+ BX_KEY_THIS s.kbd_controller.irq12_requested = 0;
+ BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0;
+
+//BX_DEBUG(( "# Okeyboard_serial_delay is %u usec",
+// (unsigned) bx_options.Okeyboard_serial_delay->get ()));
+ BX_KEY_THIS s.kbd_controller.timer_pending = 0;
+
+ // Mouse initialization stuff
+ BX_KEY_THIS s.mouse.sample_rate = 100; // reports per second
+ BX_KEY_THIS s.mouse.resolution_cpmm = 4; // 4 counts per millimeter
+ BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */
+ BX_KEY_THIS s.mouse.mode = MOUSE_MODE_RESET;
+ BX_KEY_THIS s.mouse.enable = 0;
+ BX_KEY_THIS s.mouse.delayed_dx = 0;
+ BX_KEY_THIS s.mouse.delayed_dy = 0;
+
+ for (i=0; i<BX_KBD_CONTROLLER_QSIZE; i++)
+ BX_KEY_THIS s.controller_Q[i] = 0;
+ BX_KEY_THIS s.controller_Qsize = 0;
+ BX_KEY_THIS s.controller_Qsource = 0;
+
+ // clear paste buffer
+ BX_KEY_THIS pastebuf = NULL;
+ BX_KEY_THIS pastebuf_len = 0;
+ BX_KEY_THIS pastebuf_ptr = 0;
+ BX_KEY_THIS paste_delay_changed ();
+ BX_KEY_THIS stop_paste = 0;
+
+ // mouse port installed on system board
+ DEV_cmos_set_reg(0x14, DEV_cmos_get_reg(0x14) | 0x04);
+
+#if BX_WITH_WX
+ static bx_bool first_time = 1;
+ if (first_time) {
+ first_time = 0;
+ // register shadow params (Experimental, not a complete list by far)
+ bx_list_c *list = new bx_list_c (BXP_KBD_PARAMETERS, "Keyboard State", "", 20);
+ list->add (new bx_shadow_bool_c (BXP_KBD_IRQ1_REQ,
+ "Keyboard IRQ1 requested: ", "",
+ &BX_KEY_THIS s.kbd_controller.irq1_requested));
+ list->add (new bx_shadow_bool_c (BXP_KBD_IRQ12_REQ,
+ "Keyboard IRQ12 requested: ", "",
+ &BX_KEY_THIS s.kbd_controller.irq12_requested));
+ list->add (new bx_shadow_num_c (BXP_KBD_TIMER_PENDING,
+ "Keyboard timer pending: ", "",
+ &BX_KEY_THIS s.kbd_controller.timer_pending));
+ list->add (new bx_shadow_bool_c (BXP_KBD_PARE,
+ "Keyboard PARE", "",
+ &BX_KEY_THIS s.kbd_controller.pare));
+ list->add (new bx_shadow_bool_c (BXP_KBD_TIM,
+ "Keyboard TIM", "",
+ &BX_KEY_THIS s.kbd_controller.tim));
+ list->add (new bx_shadow_bool_c (BXP_KBD_AUXB,
+ "Keyboard AUXB", "",
+ &BX_KEY_THIS s.kbd_controller.auxb));
+ list->add (new bx_shadow_bool_c (BXP_KBD_KEYL,
+ "Keyboard KEYL", "",
+ &BX_KEY_THIS s.kbd_controller.keyl));
+ list->add (new bx_shadow_bool_c (BXP_KBD_C_D,
+ "Keyboard C_D", "",
+ &BX_KEY_THIS s.kbd_controller.c_d));
+ list->add (new bx_shadow_bool_c (BXP_KBD_SYSF,
+ "Keyboard SYSF", "",
+ &BX_KEY_THIS s.kbd_controller.sysf));
+ list->add (new bx_shadow_bool_c (BXP_KBD_INPB,
+ "Keyboard INPB", "",
+ &BX_KEY_THIS s.kbd_controller.inpb));
+ list->add (new bx_shadow_bool_c (BXP_KBD_OUTB,
+ "Keyboard OUTB", "",
+ &BX_KEY_THIS s.kbd_controller.outb));
+ }
+#endif
+}
+
+ void
+bx_keyb_c::reset(unsigned type)
+{
+ if (BX_KEY_THIS pastebuf != NULL) {
+ BX_KEY_THIS stop_paste = 1;
+ }
+}
+
+ void
+bx_keyb_c::paste_delay_changed()
+{
+ BX_KEY_THIS pastedelay = bx_options.Okeyboard_paste_delay->get()/BX_IODEV_HANDLER_PERIOD;
+ BX_INFO(("will paste characters every %d keyboard ticks",BX_KEY_THIS pastedelay));
+}
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+// read function - the big picture:
+// if address == data port then
+// if byte for mouse then return it
+// else if byte for keyboard then return it
+// else address== status port
+// assemble the status bits and return them.
+//
+ Bit32u
+bx_keyb_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_KEY_SMF
+ bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_keyb_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_KEY_SMF
+
+//BX_DEBUG(( "read from port 0x%04x", (unsigned) address));
+
+ if (address == 0x60) { /* output buffer */
+ Bit8u val;
+ if (BX_KEY_THIS s.kbd_controller.auxb) { /* mouse byte available */
+ val = BX_KEY_THIS s.kbd_controller.aux_output_buffer;
+ BX_KEY_THIS s.kbd_controller.aux_output_buffer = 0;
+ // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 0;
+ BX_KEY_THIS s.kbd_controller.auxb = 0;
+ BX_KEY_THIS s.kbd_controller.irq12_requested = 0;
+
+ if (BX_KEY_THIS s.controller_Qsize) {
+ unsigned i;
+ BX_KEY_THIS s.kbd_controller.aux_output_buffer = BX_KEY_THIS s.controller_Q[0];
+ // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 1;
+ BX_KEY_THIS s.kbd_controller.auxb = 1;
+ if (BX_KEY_THIS s.kbd_controller.allow_irq12)
+ BX_KEY_THIS s.kbd_controller.irq12_requested = 1;
+ for (i=0; i<BX_KEY_THIS s.controller_Qsize-1; i++) {
+ // move Q elements towards head of queue by one
+ BX_KEY_THIS s.controller_Q[i] = BX_KEY_THIS s.controller_Q[i+1];
+ }
+ BX_KEY_THIS s.controller_Qsize--;
+ }
+
+//BX_DEBUG(("mouse: ___io_read aux = 0x%02x", (unsigned) val));
+
+ DEV_pic_lower_irq(12);
+ activate_timer();
+ BX_DEBUG(("READ(%02x) (from mouse) = %02x", (unsigned) address,
+ (unsigned) val));
+ return val;
+ }
+ else if (BX_KEY_THIS s.kbd_controller.outb) { /* kbd byte available */
+ val = BX_KEY_THIS s.kbd_controller.kbd_output_buffer;
+ // BX_INFO(("kbd: %04d outb 0 auxb 0",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 0;
+ BX_KEY_THIS s.kbd_controller.auxb = 0;
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 0;
+//BX_DEBUG(( "___io_read kbd"));
+
+ if (BX_KEY_THIS s.controller_Qsize) {
+ unsigned i;
+ BX_KEY_THIS s.kbd_controller.aux_output_buffer = BX_KEY_THIS s.controller_Q[0];
+ // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 1;
+ BX_KEY_THIS s.kbd_controller.auxb = 1;
+ if (BX_KEY_THIS s.kbd_controller.allow_irq1)
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 1;
+ for (i=0; i<BX_KEY_THIS s.controller_Qsize-1; i++) {
+ // move Q elements towards head of queue by one
+ BX_KEY_THIS s.controller_Q[i] = BX_KEY_THIS s.controller_Q[i+1];
+ }
+ BX_DEBUG(("s.controller_Qsize: %02X",BX_KEY_THIS s.controller_Qsize));
+ BX_KEY_THIS s.controller_Qsize--;
+ }
+
+ DEV_pic_lower_irq(1);
+ activate_timer();
+ BX_DEBUG(("READ(%02x) = %02x", (unsigned) address,
+ (unsigned) val));
+ return val;
+ }
+ else {
+ BX_DEBUG(("num_elements = %d", BX_KEY_THIS s.kbd_internal_buffer.num_elements));
+ BX_DEBUG(("read from port 60h with outb empty"));
+// val = BX_KEY_THIS s.kbd_controller.kbd_output_buffer;
+ return BX_KEY_THIS s.kbd_controller.kbd_output_buffer;
+ }
+ }
+
+#if BX_CPU_LEVEL >= 2
+ else if (address == 0x64) { /* status register */
+
+ return (BX_KEY_THIS s.kbd_controller.pare << 7) |
+ (BX_KEY_THIS s.kbd_controller.tim << 6) |
+ (BX_KEY_THIS s.kbd_controller.auxb << 5) |
+ (BX_KEY_THIS s.kbd_controller.keyl << 4) |
+ (BX_KEY_THIS s.kbd_controller.c_d << 3) |
+ (BX_KEY_THIS s.kbd_controller.sysf << 2) |
+ (BX_KEY_THIS s.kbd_controller.inpb << 1) |
+ BX_KEY_THIS s.kbd_controller.outb;
+ }
+
+#else /* BX_CPU_LEVEL > 0 */
+ /* XT MODE, System 8255 Mode Register */
+ else if (address == 0x64) { /* status register */
+ BX_DEBUG(("IO read from port 64h, system 8255 mode register"));
+ return BX_KEY_THIS s.kbd_controller.outb;
+ }
+#endif /* BX_CPU_LEVEL > 0 */
+
+ BX_PANIC(("unknown address in io read to keyboard port %x",
+ (unsigned) address));
+ return 0; /* keep compiler happy */
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_keyb_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_KEY_SMF
+ bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_keyb_c::write( Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_KEY_SMF
+ Bit8u command_byte;
+ static int kbd_initialized=0;
+
+ BX_DEBUG(("keyboard: 8-bit write to %04x = %02x", (unsigned)address, (unsigned)value));
+
+ switch (address) {
+ case 0x60: // input buffer
+ // if expecting data byte from command last sent to port 64h
+ if (BX_KEY_THIS s.kbd_controller.expecting_port60h) {
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 0;
+ // data byte written last to 0x60
+ BX_KEY_THIS s.kbd_controller.c_d = 0;
+ if (BX_KEY_THIS s.kbd_controller.inpb) {
+ BX_PANIC(("write to port 60h, not ready for write"));
+ }
+ switch (BX_KEY_THIS s.kbd_controller.last_comm) {
+ case 0x60: // write command byte
+ {
+ bx_bool scan_convert, disable_keyboard,
+ disable_aux;
+
+ scan_convert = (value >> 6) & 0x01;
+ disable_aux = (value >> 5) & 0x01;
+ disable_keyboard = (value >> 4) & 0x01;
+ BX_KEY_THIS s.kbd_controller.sysf = (value >> 2) & 0x01;
+ BX_KEY_THIS s.kbd_controller.allow_irq1 = (value >> 0) & 0x01;
+ BX_KEY_THIS s.kbd_controller.allow_irq12 = (value >> 1) & 0x01;
+ set_kbd_clock_enable(!disable_keyboard);
+ set_aux_clock_enable(!disable_aux);
+ if (BX_KEY_THIS s.kbd_controller.allow_irq12 && BX_KEY_THIS s.kbd_controller.auxb)
+ BX_KEY_THIS s.kbd_controller.irq12_requested = 1;
+ else if (BX_KEY_THIS s.kbd_controller.allow_irq1 && BX_KEY_THIS s.kbd_controller.outb)
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 1;
+
+ BX_DEBUG(( " allow_irq12 set to %u", (unsigned)
+ BX_KEY_THIS s.kbd_controller.allow_irq12));
+ if ( !scan_convert )
+ BX_ERROR(("keyboard: (mch) scan convert turned off"));
+
+ // (mch) NT needs this
+ BX_KEY_THIS s.kbd_controller.scancodes_translate = scan_convert;
+ }
+ break;
+ case 0xd1: // write output port
+ BX_DEBUG(("write output port with value %02xh",
+ (unsigned) value));
+ BX_SET_ENABLE_A20( (value & 0x02) != 0 );
+ if (!(value & 0x01))
+ BX_PANIC(("IO write: processor reset requested!"));
+ break;
+ case 0xd4: // Write to mouse
+ // I don't think this enables the AUX clock
+ //set_aux_clock_enable(1); // enable aux clock line
+ kbd_ctrl_to_mouse(value);
+ // ??? should I reset to previous value of aux enable?
+ break;
+
+ case 0xd3: // write mouse output buffer
+ // Queue in mouse output buffer
+ controller_enQ(value, 1);
+ break;
+
+ case 0xd2:
+ // Queue in keyboard output buffer
+ controller_enQ(value, 0);
+ break;
+
+ default:
+ BX_PANIC(("=== unsupported write to port 60h(lastcomm=%02x): %02x",
+ (unsigned) BX_KEY_THIS s.kbd_controller.last_comm, (unsigned) value));
+ }
+ }
+ else {
+ // data byte written last to 0x60
+ BX_KEY_THIS s.kbd_controller.c_d = 0;
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 0;
+ /* pass byte to keyboard */
+ /* ??? should conditionally pass to mouse device here ??? */
+ if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled==0) {
+ BX_ERROR(("keyboard disabled & send of byte %02x to kbd",
+ (unsigned) value));
+ }
+ kbd_ctrl_to_kbd(value);
+ }
+ break;
+
+ case 0x64: // control register
+ // command byte written last to 0x64
+ BX_KEY_THIS s.kbd_controller.c_d = 1;
+ BX_KEY_THIS s.kbd_controller.last_comm = value;
+ // most commands NOT expecting port60 write next
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 0;
+
+ switch (value) {
+ case 0x20: // get keyboard command byte
+ BX_DEBUG(("get keyboard command byte"));
+ // controller output buffer must be empty
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+ BX_ERROR(("kbd: OUTB set and command 0x%02x encountered", value));
+ break;
+ }
+ command_byte =
+ (BX_KEY_THIS s.kbd_controller.scancodes_translate << 6) |
+ ((!BX_KEY_THIS s.kbd_controller.aux_clock_enabled) << 5) |
+ ((!BX_KEY_THIS s.kbd_controller.kbd_clock_enabled) << 4) |
+ (0 << 3) |
+ (BX_KEY_THIS s.kbd_controller.sysf << 2) |
+ (BX_KEY_THIS s.kbd_controller.allow_irq12 << 1) |
+ (BX_KEY_THIS s.kbd_controller.allow_irq1 << 0);
+ controller_enQ(command_byte, 0);
+ break;
+ case 0x60: // write command byte
+ BX_DEBUG(("write command byte"));
+ // following byte written to port 60h is command byte
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 1;
+ break;
+
+ case 0xa0:
+ BX_DEBUG(("keyboard BIOS name not supported"));
+ break;
+
+ case 0xa1:
+ BX_DEBUG(("keyboard BIOS version not supported"));
+ break;
+
+ case 0xa7: // disable the aux device
+ set_aux_clock_enable(0);
+ BX_DEBUG(("aux device disabled"));
+ break;
+ case 0xa8: // enable the aux device
+ set_aux_clock_enable(1);
+ BX_DEBUG(("aux device enabled"));
+ break;
+ case 0xa9: // Test Mouse Port
+ // controller output buffer must be empty
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+ BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value));
+ break;
+ }
+ controller_enQ(0x00, 0); // no errors detected
+ break;
+ case 0xaa: // motherboard controller self test
+ BX_DEBUG(("Self Test"));
+ if( kbd_initialized == 0 )
+ {
+ BX_KEY_THIS s.controller_Qsize = 0;
+ BX_KEY_THIS s.kbd_controller.outb = 0;
+ kbd_initialized++;
+ }
+ // controller output buffer must be empty
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+ BX_ERROR(("kbd: OUTB set and command 0x%02x encountered", value));
+ break;
+ }
+ // (mch) Why is this commented out??? Enabling
+ BX_KEY_THIS s.kbd_controller.sysf = 1; // self test complete
+ controller_enQ(0x55, 0); // controller OK
+ break;
+ case 0xab: // Interface Test
+ // controller output buffer must be empty
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value));
+ break;
+ }
+ controller_enQ(0x00, 0);
+ break;
+ case 0xad: // disable keyboard
+ set_kbd_clock_enable(0);
+ BX_DEBUG(("keyboard disabled"));
+ break;
+ case 0xae: // enable keyboard
+ set_kbd_clock_enable(1);
+ BX_DEBUG(("keyboard enabled"));
+ break;
+ case 0xc0: // read input port
+ // controller output buffer must be empty
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value));
+ break;
+ }
+ // keyboard power normal
+ controller_enQ(0x00, 0);
+ break;
+ case 0xd0: // read output port: next byte read from port 60h
+ BX_DEBUG(("io write to port 64h, command d0h (partial)"));
+ // controller output buffer must be empty
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+BX_PANIC(("kbd: OUTB set and command 0x%02x encountered", value));
+ break;
+ }
+ controller_enQ(
+ (BX_KEY_THIS s.kbd_controller.auxb << 5) |
+ (BX_KEY_THIS s.kbd_controller.outb << 4) |
+ (BX_GET_ENABLE_A20() << 1) |
+ 0x01, 0);
+ break;
+
+ case 0xd1: // write output port: next byte written to port 60h
+ BX_DEBUG(("write output port"));
+ // following byte to port 60h written to output port
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 1;
+ break;
+
+ case 0xd3: // write mouse output buffer
+ //FIXME: Why was this a panic?
+ BX_DEBUG(("io write 0x64: command = 0xD3(write mouse outb)"));
+ // following byte to port 60h written to output port as mouse write.
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 1;
+ break;
+
+ case 0xd4: // write to mouse
+ BX_DEBUG(("io write 0x64: command = 0xD4 (write to mouse)"));
+ // following byte written to port 60h
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 1;
+ break;
+
+ case 0xd2: // write keyboard output buffer
+ BX_DEBUG(("io write 0x64: write keyboard output buffer"));
+ BX_KEY_THIS s.kbd_controller.expecting_port60h = 1;
+ break;
+ case 0xdd: // Disable A20 Address Line
+ BX_SET_ENABLE_A20(0);
+ break;
+ case 0xdf: // Enable A20 Address Line
+ BX_SET_ENABLE_A20(1);
+ break;
+ case 0xc1: // Continuous Input Port Poll, Low
+ case 0xc2: // Continuous Input Port Poll, High
+ case 0xe0: // Read Test Inputs
+ BX_PANIC(("io write 0x64: command = %02xh", (unsigned) value));
+ break;
+
+ case 0xfe: // System Reset, transition to real mode
+ BX_INFO(("system reset"));
+ bx_pc_system.ResetSignal( PCS_SET ); /* XXX is this right? */
+ {
+ for (int i=0; i<BX_SMP_PROCESSORS; i++)
+ BX_CPU(i)->reset(BX_RESET_HARDWARE);
+ }
+ // Use bx_pc_system if necessary bx_cpu.reset_cpu();
+ // bx_pc_system.ResetSignal( PCS_SET );
+ break;
+
+ default:
+ if (value==0xff || (value>=0xf0 && value<=0xfd)) {
+ /* useless pulse output bit commands ??? */
+ BX_DEBUG(("io write to port 64h, useless command %02x",
+ (unsigned) value));
+ return;
+ }
+ BX_PANIC(("unsupported io write to keyboard port %x, value = %x",
+ (unsigned) address, (unsigned) value));
+ break;
+ }
+ break;
+
+ default: BX_PANIC(("unknown address in bx_keyb_c::write()"));
+ }
+}
+
+// service_paste_buf() transfers data from the paste buffer to the hardware
+// keyboard buffer. It tries to transfer as many chars as possible at a
+// time, but because different chars require different numbers of scancodes
+// we have to be conservative. Note that this process depends on the
+// keymap tables to know what chars correspond to what keys, and which
+// chars require a shift or other modifier.
+void
+bx_keyb_c::service_paste_buf ()
+{
+ if (!BX_KEY_THIS pastebuf) return;
+ BX_DEBUG (("service_paste_buf: ptr at %d out of %d", BX_KEY_THIS pastebuf_ptr, BX_KEY_THIS pastebuf_len));
+ int fill_threshold = BX_KBD_ELEMENTS - 8;
+ while ( (BX_KEY_THIS pastebuf_ptr < BX_KEY_THIS pastebuf_len) && ! BX_KEY_THIS stop_paste) {
+ if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= fill_threshold)
+ return;
+ // there room in the buffer for a keypress and a key release.
+ // send one keypress and a key release.
+ Bit8u byte = BX_KEY_THIS pastebuf[BX_KEY_THIS pastebuf_ptr];
+ BXKeyEntry *entry = bx_keymap.findAsciiChar (byte);
+ if (!entry) {
+ BX_ERROR (("paste character 0x%02x ignored", byte));
+ } else {
+ BX_DEBUG (("pasting character 0x%02x. baseKey is %04x", byte, entry->baseKey));
+ if (entry->modKey != BX_KEYMAP_UNKNOWN)
+ BX_KEY_THIS gen_scancode (entry->modKey);
+ BX_KEY_THIS gen_scancode (entry->baseKey);
+ BX_KEY_THIS gen_scancode (entry->baseKey | BX_KEY_RELEASED);
+ if (entry->modKey != BX_KEYMAP_UNKNOWN)
+ BX_KEY_THIS gen_scancode (entry->modKey | BX_KEY_RELEASED);
+ }
+ BX_KEY_THIS pastebuf_ptr++;
+ }
+ // reached end of pastebuf. free the memory it was using.
+ delete [] BX_KEY_THIS pastebuf;
+ BX_KEY_THIS pastebuf = NULL;
+ BX_KEY_THIS stop_paste = 0;
+}
+
+// paste_bytes schedules an arbitrary number of ASCII characters to be
+// inserted into the hardware queue as it become available. Any previous
+// paste which is still in progress will be thrown out. BYTES is a pointer
+// to a region of memory containing the chars to be pasted. When the paste
+// is complete, the keyboard code will call delete [] bytes;
+void
+bx_keyb_c::paste_bytes (Bit8u *bytes, Bit32s length)
+{
+ BX_DEBUG (("paste_bytes: %d bytes", length));
+ if (BX_KEY_THIS pastebuf) {
+ BX_ERROR (("previous paste was not completed! %d chars lost",
+ BX_KEY_THIS pastebuf_len - BX_KEY_THIS pastebuf_ptr));
+ delete [] BX_KEY_THIS pastebuf; // free the old paste buffer
+ }
+ BX_KEY_THIS pastebuf = bytes;
+ BX_KEY_THIS pastebuf_ptr = 0;
+ BX_KEY_THIS pastebuf_len = length;
+ BX_KEY_THIS service_paste_buf ();
+}
+
+ void
+bx_keyb_c::gen_scancode(Bit32u key)
+{
+ unsigned char *scancode;
+ Bit8u i;
+
+ BX_DEBUG(( "gen_scancode(): %s %s", bx_keymap.getBXKeyName(key), (key >> 31)?"released":"pressed"));
+
+ if (!BX_KEY_THIS s.kbd_controller.scancodes_translate)
+ BX_DEBUG(("keyboard: gen_scancode with scancode_translate cleared"));
+
+ // Ignore scancode if keyboard clock is driven low
+ if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled==0)
+ return;
+
+ // Ignore scancode if scanning is disabled
+ if (BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled==0)
+ return;
+
+ // Switch between make and break code
+ if (key & BX_KEY_RELEASED)
+ scancode=(unsigned char *)scancodes[(key&0xFF)][BX_KEY_THIS s.kbd_controller.current_scancodes_set].brek;
+ else
+ scancode=(unsigned char *)scancodes[(key&0xFF)][BX_KEY_THIS s.kbd_controller.current_scancodes_set].make;
+
+ if (BX_KEY_THIS s.kbd_controller.scancodes_translate) {
+ // Translate before send
+ Bit8u escaped=0x00;
+
+ for (i=0; i<strlen( (const char *)scancode ); i++) {
+ if (scancode[i] == 0xF0)
+ escaped=0x80;
+ else {
+ BX_DEBUG(("gen_scancode(): writing translated %02x",translation8042[scancode[i] ] | escaped));
+ kbd_enQ(translation8042[scancode[i] ] | escaped );
+ escaped=0x00;
+ }
+ }
+ }
+ else {
+ // Send raw data
+ for (i=0; i<strlen( (const char *)scancode ); i++) {
+ BX_DEBUG(("gen_scancode(): writing raw %02x",scancode[i]));
+ kbd_enQ( scancode[i] );
+ }
+ }
+}
+
+
+
+ void BX_CPP_AttrRegparmN(1)
+bx_keyb_c::set_kbd_clock_enable(Bit8u value)
+{
+ bx_bool prev_kbd_clock_enabled;
+
+ if (value==0) {
+ BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 0;
+ }
+ else {
+ /* is another byte waiting to be sent from the keyboard ? */
+ prev_kbd_clock_enabled = BX_KEY_THIS s.kbd_controller.kbd_clock_enabled;
+ BX_KEY_THIS s.kbd_controller.kbd_clock_enabled = 1;
+
+ if (prev_kbd_clock_enabled==0 && BX_KEY_THIS s.kbd_controller.outb==0) {
+ activate_timer();
+ }
+ }
+}
+
+
+
+ void
+bx_keyb_c::set_aux_clock_enable(Bit8u value)
+{
+ bx_bool prev_aux_clock_enabled;
+
+ BX_DEBUG(("set_aux_clock_enable(%u)", (unsigned) value));
+ if (value==0) {
+ BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 0;
+ }
+ else {
+ /* is another byte waiting to be sent from the keyboard ? */
+ prev_aux_clock_enabled = BX_KEY_THIS s.kbd_controller.aux_clock_enabled;
+ BX_KEY_THIS s.kbd_controller.aux_clock_enabled = 1;
+ if (prev_aux_clock_enabled==0 && BX_KEY_THIS s.kbd_controller.outb==0)
+ activate_timer();
+ }
+}
+
+ Bit8u
+bx_keyb_c::get_kbd_enable(void)
+{
+ BX_DEBUG(("get_kbd_enable(): getting kbd_clock_enabled of: %02x",
+ (unsigned) BX_KEY_THIS s.kbd_controller.kbd_clock_enabled));
+
+ return(BX_KEY_THIS s.kbd_controller.kbd_clock_enabled);
+}
+
+ void
+bx_keyb_c::controller_enQ(Bit8u data, unsigned source)
+{
+ // source is 0 for keyboard, 1 for mouse
+
+ BX_DEBUG(("controller_enQ(%02x) source=%02x", (unsigned) data,source));
+
+ if (BX_KEY_THIS s.kbd_controller.outb)
+ BX_ERROR(("controller_enQ(): OUTB set!"));
+
+ // see if we need to Q this byte from the controller
+ // remember this includes mouse bytes.
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+ if (BX_KEY_THIS s.controller_Qsize >= BX_KBD_CONTROLLER_QSIZE)
+ BX_PANIC(("controller_enq(): controller_Q full!"));
+ BX_KEY_THIS s.controller_Q[BX_KEY_THIS s.controller_Qsize++] = data;
+ BX_KEY_THIS s.controller_Qsource = source;
+ return;
+ }
+
+ // the Q is empty
+ if (source == 0) { // keyboard
+ BX_KEY_THIS s.kbd_controller.kbd_output_buffer = data;
+ // BX_INFO(("kbd: %04d outb 1 auxb 0",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 1;
+ BX_KEY_THIS s.kbd_controller.auxb = 0;
+ BX_KEY_THIS s.kbd_controller.inpb = 0;
+ if (BX_KEY_THIS s.kbd_controller.allow_irq1)
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 1;
+ }
+ else { // mouse
+ BX_KEY_THIS s.kbd_controller.aux_output_buffer = data;
+ // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 1;
+ BX_KEY_THIS s.kbd_controller.auxb = 1;
+ BX_KEY_THIS s.kbd_controller.inpb = 0;
+ if (BX_KEY_THIS s.kbd_controller.allow_irq12)
+ BX_KEY_THIS s.kbd_controller.irq12_requested = 1;
+ }
+}
+
+void
+bx_keyb_c::kbd_enQ_imm(Bit8u val)
+{
+ int tail;
+
+ if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) {
+ BX_PANIC(("internal keyboard buffer full (imm)"));
+ return;
+ }
+
+ /* enqueue scancode in multibyte internal keyboard buffer */
+ tail = (BX_KEY_THIS s.kbd_internal_buffer.head + BX_KEY_THIS s.kbd_internal_buffer.num_elements) %
+ BX_KBD_ELEMENTS;
+
+ BX_KEY_THIS s.kbd_controller.kbd_output_buffer = val;
+ // BX_INFO(("kbd: %04d outb 1",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 1;
+
+ if (BX_KEY_THIS s.kbd_controller.allow_irq1)
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 1;
+}
+
+
+ void
+bx_keyb_c::kbd_enQ(Bit8u scancode)
+{
+ int tail;
+
+ BX_DEBUG(("kbd_enQ(0x%02x)", (unsigned) scancode));
+
+ if (BX_KEY_THIS s.kbd_internal_buffer.num_elements >= BX_KBD_ELEMENTS) {
+ BX_INFO(("internal keyboard buffer full, ignoring scancode.(%02x)",
+ (unsigned) scancode));
+ return;
+ }
+
+ /* enqueue scancode in multibyte internal keyboard buffer */
+ BX_DEBUG(("kbd_enQ: putting scancode 0x%02x in internal buffer",
+ (unsigned) scancode));
+ tail = (BX_KEY_THIS s.kbd_internal_buffer.head + BX_KEY_THIS s.kbd_internal_buffer.num_elements) %
+ BX_KBD_ELEMENTS;
+ BX_KEY_THIS s.kbd_internal_buffer.buffer[tail] = scancode;
+ BX_KEY_THIS s.kbd_internal_buffer.num_elements++;
+
+ if (!BX_KEY_THIS s.kbd_controller.outb && BX_KEY_THIS s.kbd_controller.kbd_clock_enabled) {
+ activate_timer();
+ BX_DEBUG(("activating timer..."));
+ return;
+ }
+//BX_DEBUG(( "# not activating timer...");
+//BX_DEBUG(( "# allow_irq1 = %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq1);
+//BX_DEBUG(( "# outb = %u", (unsigned) BX_KEY_THIS s.kbd_controller.outb);
+//BX_DEBUG(( "# clock_enab = %u", (unsigned) BX_KEY_THIS s.kbd_controller.kbd_clock_enabled);
+//BX_DEBUG(( "# out_buffer = %u", (unsigned) BX_KEY_THIS s.kbd_controller.kbd_output_buffer);
+}
+
+ bx_bool BX_CPP_AttrRegparmN(3)
+bx_keyb_c::mouse_enQ_packet(Bit8u b1, Bit8u b2, Bit8u b3)
+{
+ if ((BX_KEY_THIS s.mouse_internal_buffer.num_elements + 3) >= BX_MOUSE_BUFF_SIZE) {
+ return(0); /* buffer doesn't have the space */
+ }
+
+//BX_DEBUG(("mouse: enQ_packet(%02x, %02x, %02x)",
+// (unsigned) b1, (unsigned) b2, (unsigned) b3));
+
+ mouse_enQ(b1);
+ mouse_enQ(b2);
+ mouse_enQ(b3);
+ return(1);
+}
+
+
+ void
+bx_keyb_c::mouse_enQ(Bit8u mouse_data)
+{
+ int tail;
+
+ BX_DEBUG(("mouse_enQ(%02x)", (unsigned) mouse_data));
+
+ if (BX_KEY_THIS s.mouse_internal_buffer.num_elements >= BX_MOUSE_BUFF_SIZE) {
+ BX_ERROR(("mouse: internal mouse buffer full, ignoring mouse data.(%02x)",
+ (unsigned) mouse_data));
+ return;
+ }
+//BX_DEBUG(( "# mouse_enq() aux_clock_enabled = %u",
+// (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled);
+
+ /* enqueue mouse data in multibyte internal mouse buffer */
+ tail = (BX_KEY_THIS s.mouse_internal_buffer.head + BX_KEY_THIS s.mouse_internal_buffer.num_elements) %
+ BX_MOUSE_BUFF_SIZE;
+ BX_KEY_THIS s.mouse_internal_buffer.buffer[tail] = mouse_data;
+ BX_KEY_THIS s.mouse_internal_buffer.num_elements++;
+
+ if (!BX_KEY_THIS s.kbd_controller.outb && BX_KEY_THIS s.kbd_controller.aux_clock_enabled) {
+ activate_timer();
+//BX_DEBUG(( "# activating timer...");
+ return;
+ }
+//BX_DEBUG(( "# not activating timer...");
+//BX_DEBUG(( "# allow_irq12= %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12);
+//BX_DEBUG(( "# outb = %u", (unsigned) BX_KEY_THIS s.kbd_controller.outb);
+//BX_DEBUG(( "# clock_enab = %u", (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled);
+//BX_DEBUG(( "# out_buffer = %u", (unsigned) BX_KEY_THIS s.kbd_controller.aux_output_buffer);
+}
+
+ void
+bx_keyb_c::kbd_ctrl_to_kbd(Bit8u value)
+{
+
+ BX_DEBUG(("controller passed byte %02xh to keyboard", value));
+
+ if (BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic) {
+ BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 0;
+ BX_KEY_THIS s.kbd_internal_buffer.delay = (value >> 5) & 0x03;
+ switch (BX_KEY_THIS s.kbd_internal_buffer.delay) {
+ case 0: BX_INFO(("setting delay to 250 mS (unused)")); break;
+ case 1: BX_INFO(("setting delay to 500 mS (unused)")); break;
+ case 2: BX_INFO(("setting delay to 750 mS (unused)")); break;
+ case 3: BX_INFO(("setting delay to 1000 mS (unused)")); break;
+ }
+ BX_KEY_THIS s.kbd_internal_buffer.repeat_rate = value & 0x1f;
+ double cps = 1 /((double)(8 + (value & 0x07)) * (double)exp(log((double)2) * (double)((value >> 3) & 0x03)) * 0.00417);
+ BX_INFO(("setting repeat rate to %.1f cps (unused)", cps));
+ kbd_enQ(0xFA); // send ACK
+ return;
+ }
+
+ if (BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write) {
+ BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 0;
+ BX_KEY_THIS s.kbd_internal_buffer.led_status = value;
+ BX_DEBUG(("LED status set to %02x",
+ (unsigned) BX_KEY_THIS s.kbd_internal_buffer.led_status));
+ kbd_enQ(0xFA); // send ACK %%%
+ return;
+ }
+
+ if (BX_KEY_THIS s.kbd_controller.expecting_scancodes_set) {
+ BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 0;
+ if( value != 0 ) {
+ if( value<4 ) {
+ BX_KEY_THIS s.kbd_controller.current_scancodes_set = (value-1);
+ BX_INFO(("Switched to scancode set %d\n",
+ (unsigned) BX_KEY_THIS s.kbd_controller.current_scancodes_set + 1));
+ kbd_enQ(0xFA);
+ }
+ else {
+ BX_ERROR(("Received scancodes set out of range: %d\n", value ));
+ kbd_enQ(0xFF); // send ERROR
+ }
+ }
+ else {
+ // Send current scancodes set to port 0x60
+ kbd_enQ( 1 + (BX_KEY_THIS s.kbd_controller.current_scancodes_set) );
+ }
+ return;
+ }
+
+ switch (value) {
+ case 0x00: // ??? ignore and let OS timeout with no response
+ kbd_enQ(0xFA); // send ACK %%%
+ return;
+ break;
+
+ case 0x05: // ???
+ // (mch) trying to get this to work...
+ BX_KEY_THIS s.kbd_controller.sysf = 1;
+ kbd_enQ_imm(0xfe);
+ return;
+ break;
+
+ case 0xed: // LED Write
+ BX_KEY_THIS s.kbd_internal_buffer.expecting_led_write = 1;
+ kbd_enQ_imm(0xFA); // send ACK %%%
+ return;
+ break;
+
+ case 0xee: // echo
+ kbd_enQ(0xEE); // return same byte (EEh) as echo diagnostic
+ return;
+ break;
+
+ case 0xf0: // Select alternate scan code set
+ BX_KEY_THIS s.kbd_controller.expecting_scancodes_set = 1;
+ BX_DEBUG(("Expecting scancode set info...\n"));
+ kbd_enQ(0xFA); // send ACK
+ return;
+ break;
+
+ case 0xf2: // identify keyboard
+ BX_INFO(("identify keyboard command received"));
+
+ // XT sends nothing, AT sends ACK
+ // MFII with translation sends ACK+ABh+41h
+ // MFII without translation sends ACK+ABh+83h
+ if (bx_options.Okeyboard_type->get() != BX_KBD_XT_TYPE) {
+ kbd_enQ(0xFA);
+ if (bx_options.Okeyboard_type->get() == BX_KBD_MF_TYPE) {
+ kbd_enQ(0xAB);
+
+ if(BX_KEY_THIS s.kbd_controller.scancodes_translate)
+ kbd_enQ(0x41);
+ else
+ kbd_enQ(0x83);
+ }
+ }
+ return;
+ break;
+
+ case 0xf3: // typematic info
+ BX_KEY_THIS s.kbd_internal_buffer.expecting_typematic = 1;
+ BX_INFO(("setting typematic info"));
+ kbd_enQ(0xFA); // send ACK
+ return;
+ break;
+
+ case 0xf4: // enable keyboard
+ BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1;
+ kbd_enQ(0xFA); // send ACK
+ return;
+ break;
+
+ case 0xf5: // reset keyboard to power-up settings and disable scanning
+ resetinternals(1);
+ kbd_enQ(0xFA); // send ACK
+ BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 0;
+ BX_INFO(("reset-disable command received"));
+ return;
+ break;
+
+ case 0xf6: // reset keyboard to power-up settings and enable scanning
+ resetinternals(1);
+ kbd_enQ(0xFA); // send ACK
+ BX_KEY_THIS s.kbd_internal_buffer.scanning_enabled = 1;
+ BX_INFO(("reset-enable command received"));
+ return;
+ break;
+
+ case 0xf7: // PS/2 Set All Keys To Typematic
+ case 0xf8: // PS/2 Set All Keys to Make/Break
+ case 0xf9: // PS/2 PS/2 Set All Keys to Make
+ case 0xfa: // PS/2 Set All Keys to Typematic Make/Break
+ case 0xfb: // PS/2 Set Key Type to Typematic
+ case 0xfc: // PS/2 Set Key Type to Make/Break
+ case 0xfd: // PS/2 Set Key Type to Make
+ // Silently ignore and let the OS timeout, for now.
+ // If anyone has code around that makes use of that, I can
+ // provide documentation on their behavior (ask core@ggi-project.org)
+ return;
+ break;
+
+ case 0xfe: // resend. aiiee.
+ BX_PANIC( ("got 0xFE (resend)"));
+ return;
+ break;
+
+ case 0xff: // reset: internal keyboard reset and afterwards the BAT
+ BX_DEBUG(("reset command received"));
+ resetinternals(1);
+ kbd_enQ(0xFA); // send ACK
+ kbd_enQ(0xAA); // BAT test passed
+ return;
+ break;
+
+ case 0xd3:
+ kbd_enQ(0xfa);
+ return;
+
+ default:
+ /* XXX fix this properly:
+ http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/mouse/mouse.html
+ http://sourceforge.net/tracker/index.php?func=detail&aid=422457&group_id=12580&atid=112580
+ */
+ BX_ERROR(("kbd_ctrl_to_kbd(): got value of %02x",
+ (unsigned) value));
+ kbd_enQ(0xFA); /* send ACK ??? */
+ return;
+ break;
+ }
+}
+
+ void
+bx_keyb_c::timer_handler(void *this_ptr)
+{
+ bx_keyb_c *class_ptr = (bx_keyb_c *) this_ptr;
+ unsigned retval;
+
+ // retval=class_ptr->periodic( bx_options.Okeyboard_serial_delay->get());
+ retval=class_ptr->periodic(1);
+
+ if(retval&0x01)
+ DEV_pic_raise_irq(1);
+ if(retval&0x02)
+ DEV_pic_raise_irq(12);
+}
+
+ unsigned
+bx_keyb_c::periodic( Bit32u usec_delta )
+{
+/* static int multiple=0; */
+ static unsigned count_before_paste=0;
+ Bit8u retval;
+
+ UNUSED( usec_delta );
+
+ if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled ) {
+ if(++count_before_paste>=BX_KEY_THIS pastedelay) {
+ // after the paste delay, consider adding moving more chars
+ // from the paste buffer to the keyboard buffer.
+ BX_KEY_THIS service_paste_buf ();
+ count_before_paste=0;
+ }
+ }
+
+ retval = BX_KEY_THIS s.kbd_controller.irq1_requested | (BX_KEY_THIS s.kbd_controller.irq12_requested << 1);
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 0;
+ BX_KEY_THIS s.kbd_controller.irq12_requested = 0;
+
+ if ( BX_KEY_THIS s.kbd_controller.timer_pending == 0 ) {
+ return(retval);
+ }
+
+ if ( usec_delta >= BX_KEY_THIS s.kbd_controller.timer_pending ) {
+ BX_KEY_THIS s.kbd_controller.timer_pending = 0;
+ }
+ else {
+ BX_KEY_THIS s.kbd_controller.timer_pending -= usec_delta;
+ return(retval);
+ }
+
+ if (BX_KEY_THIS s.kbd_controller.outb) {
+ return(retval);
+ }
+
+ /* nothing in outb, look for possible data xfer from keyboard or mouse */
+ if (BX_KEY_THIS s.kbd_controller.kbd_clock_enabled && BX_KEY_THIS s.kbd_internal_buffer.num_elements) {
+//BX_DEBUG(( "# servicing keyboard code");
+ BX_DEBUG(("service_keyboard: key in internal buffer waiting"));
+ BX_KEY_THIS s.kbd_controller.kbd_output_buffer =
+ BX_KEY_THIS s.kbd_internal_buffer.buffer[BX_KEY_THIS s.kbd_internal_buffer.head];
+ // BX_INFO(("kbd: %04d outb 1",__LINE__)); // das
+ BX_KEY_THIS s.kbd_controller.outb = 1;
+ // commented out since this would override the current state of the
+ // mouse buffer flag - no bug seen - just seems wrong (das)
+ // BX_KEY_THIS s.kbd_controller.auxb = 0;
+//BX_DEBUG(( "# ___kbd::periodic kbd");
+ BX_KEY_THIS s.kbd_internal_buffer.head = (BX_KEY_THIS s.kbd_internal_buffer.head + 1) %
+ BX_KBD_ELEMENTS;
+ BX_KEY_THIS s.kbd_internal_buffer.num_elements--;
+ if (BX_KEY_THIS s.kbd_controller.allow_irq1)
+ BX_KEY_THIS s.kbd_controller.irq1_requested = 1;
+ }
+ else {
+ create_mouse_packet(0);
+ if (BX_KEY_THIS s.kbd_controller.aux_clock_enabled && BX_KEY_THIS s.mouse_internal_buffer.num_elements) {
+//BX_DEBUG(( "# servicing mouse code");
+ BX_DEBUG(("service_keyboard: key(from mouse) in internal buffer waiting"));
+ BX_KEY_THIS s.kbd_controller.aux_output_buffer =
+ BX_KEY_THIS s.mouse_internal_buffer.buffer[BX_KEY_THIS s.mouse_internal_buffer.head];
+
+ // BX_INFO(("kbd: %04d outb 1 auxb 1",__LINE__)); //das
+ BX_KEY_THIS s.kbd_controller.outb = 1;
+ BX_KEY_THIS s.kbd_controller.auxb = 1;
+//BX_DEBUG(( "# ___kbd:periodic aux");
+ BX_KEY_THIS s.mouse_internal_buffer.head = (BX_KEY_THIS s.mouse_internal_buffer.head + 1) %
+ BX_MOUSE_BUFF_SIZE;
+ BX_KEY_THIS s.mouse_internal_buffer.num_elements--;
+//BX_DEBUG(( "# allow12 = %u", (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12);
+ if (BX_KEY_THIS s.kbd_controller.allow_irq12)
+ BX_KEY_THIS s.kbd_controller.irq12_requested = 1;
+ }
+ else {
+ BX_DEBUG(("service_keyboard(): no keys waiting"));
+ }
+ }
+ return(retval);
+}
+
+
+
+
+ void
+bx_keyb_c::activate_timer(void)
+{
+ if (BX_KEY_THIS s.kbd_controller.timer_pending == 0) {
+ // BX_KEY_THIS s.kbd_controller.timer_pending = bx_options.Okeyboard_serial_delay->get ();
+ BX_KEY_THIS s.kbd_controller.timer_pending = 1;
+ }
+}
+
+
+ void
+bx_keyb_c::kbd_ctrl_to_mouse(Bit8u value)
+{
+BX_DEBUG(("MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value));
+BX_DEBUG((" enable = %u", (unsigned) BX_KEY_THIS s.mouse.enable));
+BX_DEBUG((" allow_irq12 = %u",
+ (unsigned) BX_KEY_THIS s.kbd_controller.allow_irq12));
+BX_DEBUG((" aux_clock_enabled = %u",
+ (unsigned) BX_KEY_THIS s.kbd_controller.aux_clock_enabled));
+//BX_DEBUG(( "MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value));
+
+ // an ACK (0xFA) is always the first response to any valid input
+ // received from the system other than Set-Wrap-Mode & Resend-Command
+
+
+ if (BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter) {
+ BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0;
+ switch (BX_KEY_THIS s.kbd_controller.last_mouse_command) {
+ case 0xf3: // Set Mouse Sample Rate
+ BX_KEY_THIS s.mouse.sample_rate = value;
+ BX_DEBUG(("[mouse] Sampling rate set: %d Hz", value));
+ controller_enQ(0xFA, 1); // ack
+ break;
+
+ case 0xe8: // Set Mouse Resolution
+ switch (value) {
+ case 0:
+ BX_KEY_THIS s.mouse.resolution_cpmm = 1;
+ break;
+ case 1:
+ BX_KEY_THIS s.mouse.resolution_cpmm = 2;
+ break;
+ case 2:
+ BX_KEY_THIS s.mouse.resolution_cpmm = 4;
+ break;
+ case 3:
+ BX_KEY_THIS s.mouse.resolution_cpmm = 8;
+ break;
+ default:
+ BX_PANIC(("[mouse] Unknown resolution %d", value));
+ break;
+ }
+ BX_DEBUG(("[mouse] Resolution set to %d counts per mm",
+ BX_KEY_THIS s.mouse.resolution_cpmm));
+
+ controller_enQ(0xFA, 1); // ack
+ break;
+
+ default:
+ BX_PANIC(("MOUSE: unknown last command (%02xh)", (unsigned) BX_KEY_THIS s.kbd_controller.last_mouse_command));
+ }
+ } else {
+ BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 0;
+ BX_KEY_THIS s.kbd_controller.last_mouse_command = value;
+
+ // test for wrap mode first
+ if (BX_KEY_THIS s.mouse.mode == MOUSE_MODE_WRAP) {
+ // if not a reset command or reset wrap mode
+ // then just echo the byte.
+ if ((value != 0xff) && (value != 0xec)) {
+ if (bx_dbg.mouse)
+ BX_INFO(("[mouse] wrap mode: Ignoring command %0X02.",value));
+ controller_enQ(value,1);
+ // bail out
+ return;
+ }
+ }
+ switch ( value ) {
+ case 0xe6: // Set Mouse Scaling to 1:1
+ controller_enQ(0xFA, 1); // ACK
+ BX_KEY_THIS s.mouse.scaling = 2;
+ BX_DEBUG(("[mouse] Scaling set to 1:1"));
+ break;
+
+ case 0xe7: // Set Mouse Scaling to 2:1
+ controller_enQ(0xFA, 1); // ACK
+ BX_KEY_THIS s.mouse.scaling = 2;
+ BX_DEBUG(("[mouse] Scaling set to 2:1"));
+ break;
+
+ case 0xe8: // Set Mouse Resolution
+ controller_enQ(0xFA, 1); // ACK
+ BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 1;
+ break;
+
+ case 0xea: // Set Stream Mode
+ if (bx_dbg.mouse)
+ BX_INFO(("[mouse] Mouse stream mode on."));
+ BX_KEY_THIS s.mouse.mode = MOUSE_MODE_STREAM;
+ controller_enQ(0xFA, 1); // ACK
+ break;
+
+ case 0xec: // Reset Wrap Mode
+ // unless we are in wrap mode ignore the command
+ if ( BX_KEY_THIS s.mouse.mode == MOUSE_MODE_WRAP) {
+ if (bx_dbg.mouse)
+ BX_INFO(("[mouse] Mouse wrap mode off."));
+ // restore previous mode except disable stream mode reporting.
+ // ### TODO disabling reporting in stream mode
+ BX_KEY_THIS s.mouse.mode = BX_KEY_THIS s.mouse.saved_mode;
+ controller_enQ(0xFA, 1); // ACK
+ }
+ break;
+ case 0xee: // Set Wrap Mode
+ // ### TODO flush output queue.
+ // ### TODO disable interrupts if in stream mode.
+ if (bx_dbg.mouse)
+ BX_INFO(("[mouse] Mouse wrap mode on."));
+ BX_KEY_THIS s.mouse.saved_mode = BX_KEY_THIS s.mouse.mode;
+ BX_KEY_THIS s.mouse.mode = MOUSE_MODE_WRAP;
+ controller_enQ(0xFA, 1); // ACK
+ break;
+
+ case 0xf0: // Set Remote Mode (polling mode, i.e. not stream mode.)
+ if (bx_dbg.mouse)
+ BX_INFO(("[mouse] Mouse remote mode on."));
+ // ### TODO should we flush/discard/ignore any already queued packets?
+ BX_KEY_THIS s.mouse.mode = MOUSE_MODE_REMOTE;
+ controller_enQ(0xFA, 1); // ACK
+ break;
+
+
+ case 0xf2: // Read Device Type
+ controller_enQ(0xFA, 1); // ACK
+ controller_enQ(0x00, 1); // Device ID
+ BX_DEBUG(("[mouse] Read mouse ID"));
+ break;
+
+ case 0xf3: // Set Mouse Sample Rate (sample rate written to port 60h)
+ controller_enQ(0xFA, 1); // ACK
+ BX_KEY_THIS s.kbd_controller.expecting_mouse_parameter = 1;
+ break;
+
+ case 0xf4: // Enable (in stream mode)
+ BX_KEY_THIS s.mouse.enable = 1;
+ controller_enQ(0xFA, 1); // ACK
+ BX_DEBUG(("[mouse] Mouse enabled (stream mode)"));
+ break;
+
+ case 0xf5: // Disable (in stream mode)
+ BX_KEY_THIS s.mouse.enable = 0;
+ controller_enQ(0xFA, 1); // ACK
+ BX_DEBUG(("[mouse] Mouse disabled (stream mode)"));
+ break;
+
+ case 0xf6: // Set Defaults
+ BX_KEY_THIS s.mouse.sample_rate = 100; /* reports per second (default) */
+ BX_KEY_THIS s.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */
+ BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */
+ BX_KEY_THIS s.mouse.enable = 0;
+ BX_KEY_THIS s.mouse.mode = MOUSE_MODE_STREAM;
+ controller_enQ(0xFA, 1); // ACK
+ BX_DEBUG(("[mouse] Set Defaults"));
+ break;
+
+ case 0xff: // Reset
+ BX_KEY_THIS s.mouse.sample_rate = 100; /* reports per second (default) */
+ BX_KEY_THIS s.mouse.resolution_cpmm = 4; /* 4 counts per millimeter (default) */
+ BX_KEY_THIS s.mouse.scaling = 1; /* 1:1 (default) */
+ BX_KEY_THIS s.mouse.mode = MOUSE_MODE_RESET;
+ BX_KEY_THIS s.mouse.enable = 0;
+ /* (mch) NT expects an ack here */
+ controller_enQ(0xFA, 1); // ACK
+ controller_enQ(0xAA, 1); // completion code
+ controller_enQ(0x00, 1); // ID code (normal mouse, wheelmouse has id 0x3)
+ BX_DEBUG(("[mouse] Mouse reset"));
+ break;
+
+ case 0xe9: // Get mouse information
+ // should we ack here? (mch): Yes
+ controller_enQ(0xFA, 1); // ACK
+ controller_enQ(BX_KEY_THIS s.mouse.get_status_byte(), 1); // status
+ controller_enQ(BX_KEY_THIS s.mouse.get_resolution_byte(), 1); // resolution
+ controller_enQ(BX_KEY_THIS s.mouse.sample_rate, 1); // sample rate
+ BX_DEBUG(("[mouse] Get mouse information"));
+ break;
+
+ case 0xeb: // Read Data (send a packet when in Remote Mode)
+ controller_enQ(0xFA, 1); // ACK
+ // perhaps we should be adding some movement here.
+ mouse_enQ_packet( ((BX_KEY_THIS s.mouse.button_status & 0x0f) | 0x08),
+ 0x00, 0x00 ); // bit3 of first byte always set
+ //assumed we really aren't in polling mode, a rather odd assumption.
+ BX_ERROR(("[mouse] Warning: Read Data command partially supported."));
+ break;
+
+ default:
+ //FEh Resend
+ BX_PANIC(("MOUSE: kbd_ctrl_to_mouse(%02xh)", (unsigned) value));
+ }
+ }
+}
+
+void
+bx_keyb_c::create_mouse_packet(bool force_enq) {
+ Bit8u b1, b2, b3;
+
+ // BX_DEBUG("Calling create_mouse_packet: force_enq=%d\n",force_enq);
+
+ if(BX_KEY_THIS s.mouse_internal_buffer.num_elements && !force_enq)
+ return;
+
+ // BX_DEBUG("Got to first milestone: force_enq=%d\n",force_enq);
+
+ Bit16s delta_x = BX_KEY_THIS s.mouse.delayed_dx;
+ Bit16s delta_y = BX_KEY_THIS s.mouse.delayed_dy;
+ Bit8u button_state=BX_KEY_THIS s.mouse.button_status | 0x08;
+
+ if(!force_enq && !delta_x && !delta_y) {
+ return;
+ }
+
+ // BX_DEBUG("Got to second milestone: delta_x=%d, delta_y=%d\n",delta_x,delta_y);
+
+ if(delta_x>254) delta_x=254;
+ if(delta_x<-254) delta_x=-254;
+ if(delta_y>254) delta_y=254;
+ if(delta_y<-254) delta_y=-254;
+
+ b1 = (button_state & 0x0f) | 0x08; // bit3 always set
+
+ if ( (delta_x>=0) && (delta_x<=255) ) {
+ b2 = (Bit8u) delta_x;
+ BX_KEY_THIS s.mouse.delayed_dx-=delta_x;
+ }
+ else if ( delta_x > 255 ) {
+ b2 = (Bit8u) 0xff;
+ BX_KEY_THIS s.mouse.delayed_dx-=255;
+ }
+ else if ( delta_x >= -256 ) {
+ b2 = (Bit8u) delta_x;
+ b1 |= 0x10;
+ BX_KEY_THIS s.mouse.delayed_dx-=delta_x;
+ }
+ else {
+ b2 = (Bit8u) 0x00;
+ b1 |= 0x10;
+ BX_KEY_THIS s.mouse.delayed_dx+=256;
+ }
+
+ if ( (delta_y>=0) && (delta_y<=255) ) {
+ b3 = (Bit8u) delta_y;
+ BX_KEY_THIS s.mouse.delayed_dy-=delta_y;
+ }
+ else if ( delta_y > 255 ) {
+ b3 = (Bit8u) 0xff;
+ BX_KEY_THIS s.mouse.delayed_dy-=255;
+ }
+ else if ( delta_y >= -256 ) {
+ b3 = (Bit8u) delta_y;
+ b1 |= 0x20;
+ BX_KEY_THIS s.mouse.delayed_dy-=delta_y;
+ }
+ else {
+ b3 = (Bit8u) 0x00;
+ b1 |= 0x20;
+ BX_KEY_THIS s.mouse.delayed_dy+=256;
+ }
+ mouse_enQ_packet(b1, b2, b3);
+}
+
+
+void
+bx_keyb_c::mouse_enabled_changed(bool enabled) {
+ if(s.mouse.delayed_dx || BX_KEY_THIS s.mouse.delayed_dy) {
+ create_mouse_packet(1);
+ }
+ s.mouse.delayed_dx=0;
+ s.mouse.delayed_dy=0;
+ BX_DEBUG(("Keyboard mouse disable called."));
+}
+
+ void
+bx_keyb_c::mouse_motion(int delta_x, int delta_y, unsigned button_state)
+{
+ bool force_enq=0;
+
+ // If mouse events are disabled on the GUI headerbar, don't
+ // generate any mouse data
+ if (bx_options.Omouse_enabled->get () == 0)
+ return;
+
+
+ // don't generate interrupts if we are in remote mode.
+ if ( BX_KEY_THIS s.mouse.mode == MOUSE_MODE_REMOTE)
+ // is there any point in doing any work if we don't act on the result
+ // so go home.
+ return;
+
+
+ // Note: enable only applies in STREAM MODE.
+ if ( BX_KEY_THIS s.mouse.enable==0 )
+ return;
+
+ // scale down the motion
+ if ( (delta_x < -1) || (delta_x > 1) )
+ delta_x /= 2;
+ if ( (delta_y < -1) || (delta_y > 1) )
+ delta_y /= 2;
+
+#ifdef VERBOSE_KBD_DEBUG
+ if (delta_x != 0 || delta_y != 0)
+ BX_DEBUG(("[mouse] Dx=%d Dy=%d", delta_x, delta_y));
+#endif /* ifdef VERBOSE_KBD_DEBUG */
+
+ if( (delta_x==0) && (delta_y==0) && (BX_KEY_THIS s.mouse.button_status == (button_state & 0x3) ) ) {
+ BX_DEBUG(("Ignoring useless mouse_motion call:\n"));
+ BX_DEBUG(("This should be fixed in the gui code.\n"));
+ return;
+ }
+
+ if(BX_KEY_THIS s.mouse.button_status != (button_state & 0x3)) {
+ force_enq=1;
+ }
+
+ BX_KEY_THIS s.mouse.button_status = button_state & 0x3;
+
+ if(delta_x>255) delta_x=255;
+ if(delta_y>255) delta_y=255;
+ if(delta_x<-256) delta_x=-256;
+ if(delta_y<-256) delta_y=-256;
+
+ BX_KEY_THIS s.mouse.delayed_dx+=delta_x;
+ BX_KEY_THIS s.mouse.delayed_dy+=delta_y;
+
+ if((BX_KEY_THIS s.mouse.delayed_dx>255)||
+ (BX_KEY_THIS s.mouse.delayed_dx<-256)||
+ (BX_KEY_THIS s.mouse.delayed_dy>255)||
+ (BX_KEY_THIS s.mouse.delayed_dy<-256)) {
+ force_enq=1;
+ }
+
+ create_mouse_packet(force_enq);
+}
+
+
+ int
+bx_keyb_c::SaveState( class state_file *fd )
+{
+ fd->write_check ("keyboard start");
+ fd->write (&BX_KEY_THIS s, sizeof (BX_KEY_THIS s));
+ fd->write_check ("keyboard end");
+ return(0);
+}
+
+
+ int
+bx_keyb_c::LoadState( class state_file *fd )
+{
+ fd->read_check ("keyboard start");
+ fd->read (&BX_KEY_THIS s, sizeof (BX_KEY_THIS s));
+ fd->read_check ("keyboard end");
+ return(0);
+}
+
diff --git a/tools/ioemu/iodev/keyboard.h b/tools/ioemu/iodev/keyboard.h
new file mode 100644
index 0000000000..24d9ccfdf0
--- /dev/null
+++ b/tools/ioemu/iodev/keyboard.h
@@ -0,0 +1,234 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: keyboard.h,v 1.22 2003/07/13 19:51:21 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#ifndef _PCKEY_H
+#define _PCKEY_H
+
+
+#define BX_KBD_ELEMENTS 16
+#define BX_MOUSE_BUFF_SIZE 48
+
+// these keywords should only be used in keyboard.cc
+#if BX_USE_KEY_SMF
+# define BX_KEY_SMF static
+# define BX_KEY_THIS theKeyboard->
+#else
+# define BX_KEY_SMF
+# define BX_KEY_THIS
+#endif
+
+#define MOUSE_MODE_RESET 10
+#define MOUSE_MODE_STREAM 11
+#define MOUSE_MODE_REMOTE 12
+#define MOUSE_MODE_WRAP 13
+
+class bx_keyb_c : public bx_keyb_stub_c {
+public:
+ bx_keyb_c(void);
+ ~bx_keyb_c(void);
+ // implement bx_devmodel_c interface
+ virtual void init(void);
+ virtual void reset(unsigned type);
+ // override stubs from bx_keyb_stub_c
+ virtual void gen_scancode(Bit32u key);
+ virtual void paste_bytes(Bit8u *data, Bit32s length);
+ virtual void mouse_motion(int delta_x, int delta_y, unsigned button_state);
+
+ // update the paste delay based on bx_options.Okeyboard_paste_delay
+ virtual void paste_delay_changed ();
+ virtual void mouse_enabled_changed(bool enabled);
+
+private:
+ BX_KEY_SMF Bit8u get_kbd_enable(void);
+ BX_KEY_SMF void service_paste_buf ();
+ BX_KEY_SMF void create_mouse_packet(bool force_enq);
+ BX_KEY_SMF void mouse_button(unsigned mouse_state);
+ BX_KEY_SMF int SaveState( class state_file *fd );
+ BX_KEY_SMF int LoadState( class state_file *fd );
+ BX_KEY_SMF unsigned periodic( Bit32u usec_delta );
+
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_KEY_SMF
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+ Bit32u read(Bit32u address, unsigned io_len);
+#endif
+
+ struct {
+ struct {
+ /* status bits matching the status port*/
+ bx_bool pare; // Bit7, 1= parity error from keyboard/mouse - ignored.
+ bx_bool tim; // Bit6, 1= timeout from keyboard - ignored.
+ bx_bool auxb; // Bit5, 1= mouse data waiting for CPU to read.
+ bx_bool keyl; // Bit4, 1= keyswitch in lock position - ignored.
+ bx_bool c_d; /* Bit3, 1=command to port 64h, 0=data to port 60h */
+ bx_bool sysf; // Bit2,
+ bx_bool inpb; // Bit1,
+ bx_bool outb; // Bit0, 1= keyboard data or mouse data ready for CPU
+ // check aux to see which. Or just keyboard
+ // data before AT style machines
+
+ /* internal to our version of the keyboard controller */
+ bx_bool kbd_clock_enabled;
+ bx_bool aux_clock_enabled;
+ bx_bool allow_irq1;
+ bx_bool allow_irq12;
+ Bit8u kbd_output_buffer;
+ Bit8u aux_output_buffer;
+ Bit8u last_comm;
+ Bit8u expecting_port60h;
+ Bit8u expecting_mouse_parameter;
+ Bit8u last_mouse_command;
+ Bit32u timer_pending;
+ bx_bool irq1_requested;
+ bx_bool irq12_requested;
+ bx_bool scancodes_translate;
+ bx_bool expecting_scancodes_set;
+ Bit8u current_scancodes_set;
+ } kbd_controller;
+
+ struct mouseStruct {
+ Bit8u sample_rate;
+ Bit8u resolution_cpmm; // resolution in counts per mm
+ Bit8u scaling;
+ Bit8u mode;
+ Bit8u saved_mode; // the mode prior to entering wrap mode
+ bx_bool enable;
+
+ Bit8u get_status_byte ()
+ {
+ // top bit is 0 , bit 6 is 1 if remote mode.
+ Bit8u ret = (Bit8u) ((mode == MOUSE_MODE_REMOTE) ? 0x40 : 0);
+ ret |= (enable << 5);
+ ret |= (scaling == 1) ? 0 : (1 << 4);
+ ret |= ((button_status & 0x1) << 2);
+ ret |= ((button_status & 0x2) << 0);
+ return ret;
+ }
+
+ Bit8u get_resolution_byte ()
+ {
+ Bit8u ret = 0;
+
+ switch (resolution_cpmm) {
+ case 1:
+ ret = 0;
+ break;
+
+ case 2:
+ ret = 1;
+ break;
+
+ case 4:
+ ret = 2;
+ break;
+
+ case 8:
+ ret = 3;
+ break;
+
+ default:
+ genlog->panic("mouse: invalid resolution_cpmm");
+ };
+ return ret;
+ }
+
+ Bit8u button_status;
+ Bit16s delayed_dx;
+ Bit16s delayed_dy;
+ } mouse;
+
+ struct {
+ int num_elements;
+ Bit8u buffer[BX_KBD_ELEMENTS];
+ int head;
+ bx_bool expecting_typematic;
+ bx_bool expecting_led_write;
+ Bit8u delay;
+ Bit8u repeat_rate;
+ Bit8u led_status;
+ bx_bool scanning_enabled;
+ } kbd_internal_buffer;
+
+ struct {
+ int num_elements;
+ Bit8u buffer[BX_MOUSE_BUFF_SIZE];
+ int head;
+ } mouse_internal_buffer;
+#define BX_KBD_CONTROLLER_QSIZE 5
+ Bit8u controller_Q[BX_KBD_CONTROLLER_QSIZE];
+ unsigned controller_Qsize;
+ unsigned controller_Qsource; // 0=keyboard, 1=mouse
+ } s; // State information for saving/loading
+
+ // The paste buffer does NOT exist in the hardware. It is a bochs
+ // construction that allows the user to "paste" arbitrary length sequences of
+ // keystrokes into the emulated machine. Since the hardware buffer is only
+ // 16 bytes, a very small amount of data can be added to the hardware buffer
+ // at a time. The paste buffer keeps track of the bytes that have not yet
+ // been pasted.
+ //
+ // Lifetime of a paste buffer: The paste data comes from the system
+ // clipboard, which must be accessed using platform independent code in the
+ // gui. Because every gui has its own way of managing the clipboard memory
+ // (in X windows, you're supposed to call Xfree for example), in the platform
+ // specific code we make a copy of the clipboard buffer with
+ // "new Bit8u[length]". Then the pointer is passed into
+ // bx_keyb_c::paste_bytes, along with the length. The gui code never touches
+ // the pastebuf again, and does not free it. The keyboard code is
+ // responsible for deallocating the paste buffer using delete [] buf. The
+ // paste buffer is binary data, and it is probably NOT null terminated.
+ //
+ // Summary: A paste buffer is allocated (new) in the platform-specific gui
+ // code, passed to the keyboard model, and is freed (delete[]) when it is no
+ // longer needed.
+ Bit8u *pastebuf; // ptr to bytes to be pasted, or NULL if none in progress
+ Bit32u pastebuf_len; // length of pastebuf
+ Bit32u pastebuf_ptr; // ptr to next byte to be added to hw buffer
+ Bit32u pastedelay; // count before paste
+ bx_bool stop_paste; // stop the current paste operation on hardware reset
+
+ BX_KEY_SMF void resetinternals(bx_bool powerup);
+ BX_KEY_SMF void set_kbd_clock_enable(Bit8u value) BX_CPP_AttrRegparmN(1);
+ BX_KEY_SMF void set_aux_clock_enable(Bit8u value);
+ BX_KEY_SMF void kbd_ctrl_to_kbd(Bit8u value);
+ BX_KEY_SMF void kbd_ctrl_to_mouse(Bit8u value);
+ BX_KEY_SMF void kbd_enQ(Bit8u scancode);
+ BX_KEY_SMF void kbd_enQ_imm(Bit8u val);
+ BX_KEY_SMF void activate_timer(void);
+ BX_KEY_SMF void controller_enQ(Bit8u data, unsigned source);
+ BX_KEY_SMF bx_bool mouse_enQ_packet(Bit8u b1, Bit8u b2, Bit8u b3) BX_CPP_AttrRegparmN(3);
+ BX_KEY_SMF void mouse_enQ(Bit8u mouse_data);
+
+ static void timer_handler(void *);
+ void timer(void);
+ int timer_handle;
+ };
+
+
+#endif // #ifndef _PCKEY_H
diff --git a/tools/ioemu/iodev/load32bitOShack.cc b/tools/ioemu/iodev/load32bitOShack.cc
new file mode 100644
index 0000000000..6a0a4904d8
--- /dev/null
+++ b/tools/ioemu/iodev/load32bitOShack.cc
@@ -0,0 +1,322 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: load32bitOShack.cc,v 1.14 2003/08/08 00:05:53 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+#include "bochs.h"
+#define LOG_THIS genlog->
+
+
+
+static void bx_load_linux_hack(void);
+static void bx_load_null_kernel_hack(void);
+static Bit32u bx_load_kernel_image(char *path, Bit32u paddr);
+
+ void
+bx_load32bitOSimagehack(void)
+{
+ // Replay IO from log to initialize IO devices to
+ // a reasonable state needed for the OS. This is done
+ // in lieu of running the 16-bit BIOS to init things,
+ // since we want to test straight 32bit stuff for
+ // freemware.
+
+#ifndef BX_USE_VMX
+ FILE *fp;
+
+ fp = fopen(bx_options.load32bitOSImage.Oiolog->getptr (), "r");
+
+ if (fp == NULL) {
+ BX_PANIC(("could not open IO init file."));
+ }
+
+ while (1) {
+ unsigned len, op, port, val;
+ int ret;
+ ret = fscanf(fp, "%u %u %x %x\n",
+ &len, &op, &port, &val);
+ if (ret != 4) {
+ BX_PANIC(("could not open IO init file."));
+ }
+ if (op == 0) {
+ // read
+ (void) bx_devices.inp(port, len);
+ }
+ else if (op == 1) {
+ // write
+ bx_devices.outp(port, val, len);
+ }
+ else {
+ BX_PANIC(("bad IO op in init filen"));
+ }
+ if (feof(fp)) break;
+ }
+#endif
+
+ // Invoke proper hack depending on which OS image we're loading
+ switch (bx_options.load32bitOSImage.OwhichOS->get ()) {
+ case Load32bitOSLinux:
+ bx_load_linux_hack();
+ break;
+ case Load32bitOSNullKernel:
+ bx_load_null_kernel_hack();
+ break;
+ default:
+ BX_PANIC(("load32bitOSImage: OS not recognized"));
+ }
+}
+
+struct gdt_entry
+{
+ Bit32u low;
+ Bit32u high;
+};
+struct linux_setup_params
+{
+ /* 0x000 */ Bit8u orig_x;
+ /* 0x001 */ Bit8u orig_y;
+ /* 0x002 */ Bit16u memory_size_std;
+ /* 0x004 */ Bit16u orig_video_page;
+ /* 0x006 */ Bit8u orig_video_mode;
+ /* 0x007 */ Bit8u orig_video_cols;
+ /* 0x008 */ Bit16u unused1;
+ /* 0x00a */ Bit16u orig_video_ega_bx;
+ /* 0x00c */ Bit16u unused2;
+ /* 0x00e */ Bit8u orig_video_lines;
+ /* 0x00f */ Bit8u orig_video_isVGA;
+ /* 0x010 */ Bit16u orig_video_points;
+ /* 0x012 */ Bit8u pad1[0x40 - 0x12];
+ /* 0x040 */ Bit8u apm_info[0x80 - 0x40];
+ /* 0x080 */ Bit8u hd0_info[16];
+ /* 0x090 */ Bit8u hd1_info[16];
+ /* 0x0a0 */ Bit8u pad2[0x1e0 - 0xa0];
+ /* 0x1e0 */ Bit32u memory_size_ext;
+ /* 0x1e4 */ Bit8u pad3[0x1f1 - 0x1e4];
+ /* 0x1f1 */ Bit8u setup_sects;
+ /* 0x1f2 */ Bit16u mount_root_rdonly;
+ /* 0x1f4 */ Bit16u sys_size;
+ /* 0x1f6 */ Bit16u swap_dev;
+ /* 0x1f8 */ Bit16u ramdisk_flags;
+ /* 0x1fa */ Bit16u vga_mode;
+ /* 0x1fc */ Bit16u orig_root_dev;
+ /* 0x1fe */ Bit16u bootsect_magic;
+ /* 0x200 */ Bit8u pad4[0x210 - 0x200];
+ /* 0x210 */ Bit32u loader_type;
+ /* 0x214 */ Bit32u kernel_start;
+ /* 0x218 */ Bit32u initrd_start;
+ /* 0x21c */ Bit32u initrd_size;
+ /* 0x220 */ Bit8u pad5[0x400 - 0x220];
+ /* 0x400 */ struct gdt_entry gdt[128];
+ /* 0x800 */ Bit8u commandline[2048];
+};
+
+ static void
+bx_load_linux_setup_params( Bit32u initrd_start, Bit32u initrd_size )
+{
+ BX_MEM_C *mem = BX_MEM(0);
+ struct linux_setup_params *params =
+ (struct linux_setup_params *) &mem->vector[0x00090000];
+
+ memset( params, '\0', sizeof(*params) );
+
+ /* Video settings (standard VGA) */
+ params->orig_x = 0;
+ params->orig_y = 0;
+ params->orig_video_page = 0;
+ params->orig_video_mode = 3;
+ params->orig_video_cols = 80;
+ params->orig_video_lines = 25;
+ params->orig_video_points = 16;
+ params->orig_video_isVGA = 1;
+ params->orig_video_ega_bx = 3;
+
+ /* Memory size (total mem - 1MB, in KB) */
+ params->memory_size_ext = (mem->megabytes - 1) * 1024;
+
+ /* Boot parameters */
+ params->loader_type = 1;
+ params->bootsect_magic = 0xaa55;
+ params->mount_root_rdonly = 0;
+ params->orig_root_dev = 0x0100;
+ params->initrd_start = initrd_start;
+ params->initrd_size = initrd_size;
+
+ /* Initial GDT */
+ params->gdt[2].high = 0x00cf9a00;
+ params->gdt[2].low = 0x0000ffff;
+ params->gdt[3].high = 0x00cf9200;
+ params->gdt[3].low = 0x0000ffff;
+}
+
+ void
+bx_load_linux_hack(void)
+{
+#ifndef BX_USE_VMX
+ Bit32u initrd_start = 0, initrd_size = 0;
+
+ // The RESET function will have been called first.
+ // Set CPU and memory features which are assumed at this point.
+
+ // Load Linux kernel image
+ bx_load_kernel_image( bx_options.load32bitOSImage.Opath->getptr (), 0x100000 );
+
+ // Load initial ramdisk image if requested
+ if ( bx_options.load32bitOSImage.Oinitrd->getptr () )
+ {
+ initrd_start = 0x00800000; /* FIXME: load at top of memory */
+ initrd_size = bx_load_kernel_image( bx_options.load32bitOSImage.Oinitrd->getptr (), initrd_start );
+ }
+
+ // Setup Linux startup parameters buffer
+ bx_load_linux_setup_params( initrd_start, initrd_size );
+#endif
+
+ // Enable A20 line
+ BX_SET_ENABLE_A20( 1 );
+
+ // Setup PICs the way Linux likes it
+ BX_OUTP( 0x20, 0x11, 1 );
+ BX_OUTP( 0xA0, 0x11, 1 );
+ BX_OUTP( 0x21, 0x20, 1 );
+ BX_OUTP( 0xA1, 0x28, 1 );
+ BX_OUTP( 0x21, 0x04, 1 );
+ BX_OUTP( 0xA1, 0x02, 1 );
+ BX_OUTP( 0x21, 0x01, 1 );
+ BX_OUTP( 0xA1, 0x01, 1 );
+ BX_OUTP( 0x21, 0xFF, 1 );
+ BX_OUTP( 0xA1, 0xFB, 1 );
+
+#ifndef BX_USE_VMX
+ // Disable interrupts and NMIs
+ BX_CPU(0)->clear_IF ();
+#endif
+
+ BX_OUTP( 0x70, 0x80, 1 );
+
+#ifndef BX_USE_VMX
+ // Enter protected mode
+ // Fixed by george (kyriazis at nvidia.com)
+ // BX_CPU(0)->cr0.pe = 1;
+ // BX_CPU(0)->cr0.val32 |= 0x01;
+
+ BX_CPU(0)->SetCR0(BX_CPU(0)->cr0.val32 | 0x01);
+
+ // load esi with real_mode
+ BX_CPU(0)->gen_reg[BX_32BIT_REG_ESI].dword.erx = 0x90000;
+
+ // Set up initial GDT
+ BX_CPU(0)->gdtr.limit = 0x400;
+ BX_CPU(0)->gdtr.base = 0x00090400;
+
+ // Jump to protected mode entry point
+ BX_CPU(0)->jump_protected( NULL, 0x10, 0x00100000 );
+#endif
+}
+
+ void
+bx_load_null_kernel_hack(void)
+{
+#ifndef BX_USE_VMX
+ // The RESET function will have been called first.
+ // Set CPU and memory features which are assumed at this point.
+
+ bx_load_kernel_image(bx_options.load32bitOSImage.Opath->getptr (), 0x100000);
+
+ // EIP deltas
+ BX_CPU(0)->prev_eip =
+ BX_CPU(0)->dword.eip = 0x00100000;
+
+ // CS deltas
+ BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.base = 0x00000000;
+ BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.limit = 0xFFFFF;
+ BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.limit_scaled = 0xFFFFFFFF;
+ BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.g = 1; // page gran
+ BX_CPU(0)->sregs[BX_SEG_REG_CS].cache.u.segment.d_b = 1; // 32bit
+
+ // DS deltas
+ BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.base = 0x00000000;
+ BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.limit = 0xFFFFF;
+ BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.limit_scaled = 0xFFFFFFFF;
+ BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.g = 1; // page gran
+ BX_CPU(0)->sregs[BX_SEG_REG_DS].cache.u.segment.d_b = 1; // 32bit
+
+ // CR0 deltas
+ BX_CPU(0)->cr0.pe = 1; // protected mode
+#endif // BX_USE_VMX
+}
+
+ Bit32u
+bx_load_kernel_image(char *path, Bit32u paddr)
+{
+ struct stat stat_buf;
+ int fd, ret;
+ unsigned long size, offset;
+ Bit32u page_size;
+
+ // read in ROM BIOS image file
+ fd = open(path, O_RDONLY
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+ if (fd < 0) {
+ BX_INFO(( "load_kernel_image: couldn't open image file '%s'.", path ));
+ BX_EXIT(1);
+ }
+ ret = fstat(fd, &stat_buf);
+ if (ret) {
+ BX_INFO(( "load_kernel_image: couldn't stat image file '%s'.", path ));
+ BX_EXIT(1);
+ }
+
+ size = stat_buf.st_size;
+ page_size = ((Bit32u)size + 0xfff) & ~0xfff;
+
+ BX_MEM_C *mem = BX_MEM(0);
+ if ( (paddr + size) > mem->len ) {
+ BX_INFO(( "load_kernel_image: address range > physical memsize!" ));
+ BX_EXIT(1);
+ }
+
+ offset = 0;
+ while (size > 0) {
+ ret = read(fd, (bx_ptr_t) &mem->vector[paddr + offset], size);
+ if (ret <= 0) {
+ BX_INFO(( "load_kernel_image: read failed on image" ));
+ BX_EXIT(1);
+ }
+ size -= ret;
+ offset += ret;
+ }
+ close(fd);
+ BX_INFO(( "#(%u) load_kernel_image: '%s', size=%u read into memory at %08x",
+ BX_SIM_ID, path,
+ (unsigned) stat_buf.st_size,
+ (unsigned) paddr ));
+
+ return page_size;
+}
diff --git a/tools/ioemu/iodev/logio.cc b/tools/ioemu/iodev/logio.cc
new file mode 100644
index 0000000000..2b79719a2c
--- /dev/null
+++ b/tools/ioemu/iodev/logio.cc
@@ -0,0 +1,631 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: logio.cc,v 1.42 2003/08/24 10:30:07 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+#include "bochs.h"
+#include <assert.h>
+#include "state_file.h"
+
+#if BX_WITH_CARBON
+#include <Carbon/Carbon.h>
+#endif
+
+// Just for the iofunctions
+
+
+int Allocio=0;
+
+void
+iofunctions::flush(void) {
+ if(logfd && magic == MAGIC_LOGNUM) {
+ fflush(logfd);
+ }
+}
+
+void
+iofunctions::init(void) {
+ // iofunctions methods must not be called before this magic
+ // number is set.
+ magic=MAGIC_LOGNUM;
+
+ // sets the default logprefix
+ strcpy(logprefix,"%t%e%d");
+ n_logfn = 0;
+ init_log(stderr);
+ log = new logfunc_t(this);
+ log->put("IO");
+ log->settype(IOLOG);
+ log->ldebug ("Init(log file: '%s').",logfn);
+}
+
+void
+iofunctions::add_logfn (logfunc_t *fn)
+{
+ assert (n_logfn < MAX_LOGFNS);
+ logfn_list[n_logfn++] = fn;
+}
+
+void
+iofunctions::set_log_action (int loglevel, int action)
+{
+ for (int i=0; i<n_logfn; i++)
+ logfn_list[i]->setonoff(loglevel, action);
+}
+
+void
+iofunctions::init_log(const char *fn)
+{
+ assert (magic==MAGIC_LOGNUM);
+ // use newfd/newfn so that we can log the message to the OLD log
+ // file descriptor.
+ FILE *newfd = stderr;
+ char *newfn = "/dev/stderr";
+ if( strcmp( fn, "-" ) != 0 ) {
+ newfd = fopen(fn, "w");
+ if(newfd != NULL) {
+ newfn = strdup(fn);
+ log->ldebug ("Opened log file '%s'.", fn );
+ } else {
+ // in constructor, genlog might not exist yet, so do it the safe way.
+ log->error("Couldn't open log file: %s, using stderr instead", fn);
+ newfd = stderr;
+ }
+ }
+ logfd = newfd;
+ logfn = newfn;
+}
+
+void
+iofunctions::init_log(FILE *fs)
+{
+ assert (magic==MAGIC_LOGNUM);
+ logfd = fs;
+
+ if(fs == stderr) {
+ logfn = "/dev/stderr";
+ } else if(fs == stdout) {
+ logfn = "/dev/stdout";
+ } else {
+ logfn = "(unknown)";
+ }
+
+}
+
+void
+iofunctions::init_log(int fd)
+{
+ assert (magic==MAGIC_LOGNUM);
+ FILE *tmpfd;
+ if( (tmpfd = fdopen(fd,"w")) == NULL ) {
+ log->panic("Couldn't open fd %d as a stream for writing", fd);
+ return;
+ }
+
+ init_log(tmpfd);
+ return;
+};
+
+// all other functions may use genlog safely.
+#define LOG_THIS genlog->
+
+// This converts the option string to a printf style string with the following args:
+// 1. timer, 2. event, 3. cpu0 eip, 4. device
+void
+iofunctions::set_log_prefix(const char* prefix) {
+
+ strcpy(logprefix,prefix);
+}
+
+// iofunctions::out( class, level, prefix, fmt, ap)
+// DO NOT nest out() from ::info() and the like.
+// fmt and ap retained for direct printinf from iofunctions only!
+
+void
+iofunctions::out(int f, int l, const char *prefix, const char *fmt, va_list ap)
+{
+ char c=' ', *s;
+ assert (magic==MAGIC_LOGNUM);
+ assert (this != NULL);
+ assert (logfd != NULL);
+
+ //if( showtick )
+ // fprintf(logfd, "%011lld", bx_pc_system.time_ticks());
+
+ switch(l) {
+ case LOGLEV_INFO: c='i'; break;
+ case LOGLEV_PANIC: c='p'; break;
+ case LOGLEV_PASS: c='s'; break;
+ case LOGLEV_ERROR: c='e'; break;
+ case LOGLEV_DEBUG: c='d'; break;
+ default: break;
+ }
+ //fprintf(logfd, "-%c",c);
+
+ //if(prefix != NULL)
+ // fprintf(logfd, "%s ", prefix);
+
+ s=logprefix;
+ while(*s) {
+ switch(*s) {
+ case '%':
+ if(*(s+1))s++;
+ else break;
+ switch(*s) {
+ case 'd':
+ fprintf(logfd, "%s", prefix==NULL?"":prefix);
+ break;
+ case 't':
+ fprintf(logfd, "%011lld", bx_pc_system.time_ticks());
+ break;
+#ifndef BX_USE_VMX
+ case 'i':
+ fprintf(logfd, "%08x", BX_CPU(0)==NULL?0:BX_CPU(0)->dword.eip);
+ break;
+#endif
+ case 'e':
+ fprintf(logfd, "%c", c);
+ break;
+ case '%':
+ fprintf(logfd,"%%");
+ break;
+ default:
+ fprintf(logfd,"%%%c",*s);
+ }
+ break;
+ default :
+ fprintf(logfd,"%c",*s);
+ }
+ s++;
+ }
+
+ fprintf(logfd," ");
+
+ if(l==LOGLEV_PANIC)
+ fprintf(logfd, ">>PANIC<< ");
+ if(l==LOGLEV_PASS)
+ fprintf(logfd, ">>PASS<< ");
+
+ vfprintf(logfd, fmt, ap);
+ fprintf(logfd, "\n");
+ fflush(logfd);
+
+ return;
+}
+
+iofunctions::iofunctions(FILE *fs)
+{
+ init();
+ init_log(fs);
+}
+
+iofunctions::iofunctions(const char *fn)
+{
+ init();
+ init_log(fn);
+}
+
+iofunctions::iofunctions(int fd)
+{
+ init();
+ init_log(fd);
+}
+
+iofunctions::iofunctions(void)
+{
+ this->init();
+}
+
+iofunctions::~iofunctions(void)
+{
+ // flush before erasing magic number, or flush does nothing.
+ this->flush();
+ this->magic=0;
+}
+
+#define LOG_THIS genlog->
+
+int logfunctions::default_onoff[N_LOGLEV] = {
+ ACT_IGNORE, // ignore debug
+ ACT_REPORT, // report info
+ ACT_REPORT, // report error
+#if BX_WITH_WX
+ ACT_ASK, // on panic, ask user what to do
+#else
+ ACT_FATAL, // on panic, quit
+#endif
+ ACT_FATAL
+};
+
+logfunctions::logfunctions(void)
+{
+ prefix = NULL;
+ put(" ");
+ settype(GENLOG);
+ if (io == NULL && Allocio == 0) {
+ Allocio = 1;
+ io = new iofunc_t(stderr);
+ }
+ setio(io);
+ // BUG: unfortunately this can be called before the bochsrc is read,
+ // which means that the bochsrc has no effect on the actions.
+ for (int i=0; i<N_LOGLEV; i++)
+ onoff[i] = get_default_action(i);
+}
+
+logfunctions::logfunctions(iofunc_t *iofunc)
+{
+ prefix = NULL;
+ put(" ");
+ settype(GENLOG);
+ setio(iofunc);
+ // BUG: unfortunately this can be called before the bochsrc is read,
+ // which means that the bochsrc has no effect on the actions.
+ for (int i=0; i<N_LOGLEV; i++)
+ onoff[i] = get_default_action(i);
+}
+
+logfunctions::~logfunctions(void)
+{
+ if ( this->prefix )
+ {
+ free(this->prefix);
+ this->prefix = NULL;
+ }
+}
+
+void
+logfunctions::setio(iofunc_t *i)
+{
+ // add pointer to iofunction object to use
+ this->logio = i;
+ // give iofunction a pointer to me
+ i->add_logfn (this);
+}
+
+void
+logfunctions::put(char *p)
+{
+ char *tmpbuf;
+ tmpbuf=strdup("[ ]");// if we ever have more than 32 chars,
+ // we need to rethink this
+
+ if ( tmpbuf == NULL)
+ {
+ return ; /* allocation not successful */
+ }
+ if ( this->prefix != NULL )
+ {
+ free(this->prefix); /* free previously allocated memory */
+ this->prefix = NULL;
+ }
+ int len=strlen(p);
+ for(int i=1;i<len+1;i++) {
+ tmpbuf[i]=p[i-1];
+ }
+
+ switch(len) {
+ case 1: tmpbuf[2]=' ';
+ case 2: tmpbuf[3]=' ';
+ case 3: tmpbuf[4]=' ';
+ case 4: tmpbuf[5]=' ';
+ default: tmpbuf[6]=']'; tmpbuf[7]='\0'; break;
+ }
+
+ this->prefix=tmpbuf;
+}
+
+void
+logfunctions::settype(int t)
+{
+ type=t;
+}
+
+void
+logfunctions::info(const char *fmt, ...)
+{
+ va_list ap;
+
+ assert (this != NULL);
+ assert (this->logio != NULL);
+
+ if(!onoff[LOGLEV_INFO]) return;
+
+ va_start(ap, fmt);
+ this->logio->out(this->type,LOGLEV_INFO,this->prefix, fmt, ap);
+ if (onoff[LOGLEV_INFO] == ACT_ASK)
+ ask (LOGLEV_INFO, this->prefix, fmt, ap);
+ if (onoff[LOGLEV_INFO] == ACT_FATAL)
+ fatal (this->prefix, fmt, ap, 1);
+ va_end(ap);
+
+}
+
+void
+logfunctions::error(const char *fmt, ...)
+{
+ va_list ap;
+
+ assert (this != NULL);
+ assert (this->logio != NULL);
+
+ if(!onoff[LOGLEV_ERROR]) return;
+
+ va_start(ap, fmt);
+ this->logio->out(this->type,LOGLEV_ERROR,this->prefix, fmt, ap);
+ if (onoff[LOGLEV_ERROR] == ACT_ASK)
+ ask (LOGLEV_ERROR, this->prefix, fmt, ap);
+ if (onoff[LOGLEV_ERROR] == ACT_FATAL)
+ fatal (this->prefix, fmt, ap, 1);
+ va_end(ap);
+}
+
+void
+logfunctions::panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ assert (this != NULL);
+ assert (this->logio != NULL);
+
+ // Special case for panics since they are so important. Always print
+ // the panic to the log, no matter what the log action says.
+ //if(!onoff[LOGLEV_PANIC]) return;
+
+ va_start(ap, fmt);
+ this->logio->out(this->type,LOGLEV_PANIC,this->prefix, fmt, ap);
+
+ // This fixes a funny bug on linuxppc where va_list is no pointer but a struct
+ va_end(ap);
+ va_start(ap, fmt);
+
+ if (onoff[LOGLEV_PANIC] == ACT_ASK)
+ ask (LOGLEV_PANIC, this->prefix, fmt, ap);
+ if (onoff[LOGLEV_PANIC] == ACT_FATAL)
+ fatal (this->prefix, fmt, ap, 1);
+ va_end(ap);
+}
+
+void
+logfunctions::pass(const char *fmt, ...)
+{
+ va_list ap;
+
+ assert (this != NULL);
+ assert (this->logio != NULL);
+
+ // Special case for panics since they are so important. Always print
+ // the panic to the log, no matter what the log action says.
+ //if(!onoff[LOGLEV_PASS]) return;
+
+ va_start(ap, fmt);
+ this->logio->out(this->type,LOGLEV_PASS,this->prefix, fmt, ap);
+
+ // This fixes a funny bug on linuxppc where va_list is no pointer but a struct
+ va_end(ap);
+ va_start(ap, fmt);
+
+ if (onoff[LOGLEV_PASS] == ACT_ASK)
+ ask (LOGLEV_PASS, this->prefix, fmt, ap);
+ if (onoff[LOGLEV_PASS] == ACT_FATAL)
+ fatal (this->prefix, fmt, ap, 101);
+ va_end(ap);
+}
+
+void
+logfunctions::ldebug(const char *fmt, ...)
+{
+ va_list ap;
+
+ assert (this != NULL);
+ assert (this->logio != NULL);
+
+ if(!onoff[LOGLEV_DEBUG]) return;
+
+ va_start(ap, fmt);
+ this->logio->out(this->type,LOGLEV_DEBUG,this->prefix, fmt, ap);
+ if (onoff[LOGLEV_DEBUG] == ACT_ASK)
+ ask (LOGLEV_DEBUG, this->prefix, fmt, ap);
+ if (onoff[LOGLEV_DEBUG] == ACT_FATAL)
+ fatal (this->prefix, fmt, ap, 1);
+ va_end(ap);
+}
+
+void
+logfunctions::ask (int level, const char *prefix, const char *fmt, va_list ap)
+{
+ // Guard against reentry on ask() function. The danger is that some
+ // function that's called within ask() could trigger another
+ // BX_PANIC that could call ask() again, leading to infinite
+ // recursion and infinite asks.
+ static char in_ask_already = 0;
+ char buf1[1024];
+ if (in_ask_already) {
+ fprintf (stderr, "logfunctions::ask() should not reenter!!\n");
+ return;
+ }
+ in_ask_already = 1;
+ vsprintf (buf1, fmt, ap);
+ // FIXME: facility set to 0 because it's unknown.
+
+ // update vga screen. This is useful because sometimes useful messages
+ // are printed on the screen just before a panic. It's also potentially
+ // dangerous if this function calls ask again... That's why I added
+ // the reentry check above.
+ if (SIM->get_init_done()) DEV_vga_refresh();
+
+#if !BX_EXTERNAL_DEBUGGER
+ // ensure the text screen is showing
+ SIM->set_display_mode (DISP_MODE_CONFIG);
+ int val = SIM->log_msg (prefix, level, buf1);
+ switch (val)
+ {
+ case BX_LOG_ASK_CHOICE_CONTINUE:
+ break;
+ case BX_LOG_ASK_CHOICE_CONTINUE_ALWAYS:
+ // user said continue, and don't "ask" for this facility again.
+ setonoff (level, ACT_REPORT);
+ break;
+ case BX_LOG_ASK_CHOICE_DIE:
+ bx_user_quit = 1;
+ in_ask_already = 0; // because fatal will longjmp out
+ fatal (prefix, fmt, ap, 1);
+ // should never get here
+ BX_PANIC (("in ask(), fatal() should never return!"));
+ break;
+ case BX_LOG_ASK_CHOICE_DUMP_CORE:
+ fprintf (stderr, "User chose to dump core...\n");
+#if BX_HAVE_ABORT
+ abort ();
+#else
+ // do something highly illegal that should kill the process.
+ // Hey, this is fun!
+ {
+ char *crashptr = (char *)0; char c = *crashptr;
+ }
+ fprintf (stderr, "Sorry, I couldn't find your abort() function. Exiting.");
+ exit (0);
+#endif
+#if BX_DEBUGGER
+ case BX_LOG_ASK_CHOICE_ENTER_DEBUG:
+ // user chose debugger. To "drop into the debugger" we just set the
+ // interrupt_requested bit and continue execution. Before the next
+ // instruction, it should notice the user interrupt and return to
+ // the debugger.
+ bx_guard.interrupt_requested = 1;
+ break;
+#endif
+ default:
+ // this happens if panics happen before the callback is initialized
+ // in gui/control.cc.
+ fprintf (stderr, "WARNING: log_msg returned unexpected value %d\n", val);
+ }
+#else
+ // external debugger ask code goes here
+#endif
+ // return to simulation mode
+ SIM->set_display_mode (DISP_MODE_SIM);
+ in_ask_already = 0;
+}
+
+#if BX_WITH_CARBON
+/* Panic button to display fatal errors.
+ Completely self contained, can't rely on carbon.cc being available */
+static void carbonFatalDialog(const char *error, const char *exposition)
+{
+ DialogRef alertDialog;
+ CFStringRef cfError;
+ CFStringRef cfExposition;
+ DialogItemIndex index;
+ AlertStdCFStringAlertParamRec alertParam = {0};
+
+ // Init libraries
+ InitCursor();
+ // Assemble dialog
+ cfError = CFStringCreateWithCString(NULL, error, kCFStringEncodingASCII);
+ if(exposition != NULL)
+ {
+ cfExposition = CFStringCreateWithCString(NULL, exposition, kCFStringEncodingASCII);
+ }
+ else { cfExposition = NULL; }
+ alertParam.version = kStdCFStringAlertVersionOne;
+ alertParam.defaultText = CFSTR("Quit");
+ alertParam.position = kWindowDefaultPosition;
+ alertParam.defaultButton = kAlertStdAlertOKButton;
+ // Display Dialog
+ CreateStandardAlert(
+ kAlertStopAlert,
+ cfError,
+ cfExposition, /* can be NULL */
+ &alertParam, /* can be NULL */
+ &alertDialog);
+ RunStandardAlert( alertDialog, NULL, &index);
+ // Cleanup
+ CFRelease( cfError );
+ if( cfExposition != NULL ) { CFRelease( cfExposition ); }
+}
+#endif
+
+void
+logfunctions::fatal (const char *prefix, const char *fmt, va_list ap, int exit_status)
+{
+ bx_atexit();
+#if BX_WITH_CARBON
+ if(!isatty(STDIN_FILENO) && !SIM->get_init_done())
+ {
+ char buf1[1024];
+ char buf2[1024];
+ vsprintf (buf1, fmt, ap);
+ sprintf (buf2, "Bochs startup error\n%s", buf1);
+ carbonFatalDialog(buf2,
+ "For more information, try running Bochs within Terminal by clicking on \"bochs.scpt\".");
+ }
+#endif
+#if !BX_WITH_WX
+ static char *divider = "========================================================================";
+ fprintf (stderr, "%s\n", divider);
+ fprintf (stderr, "Bochs is exiting with the following message:\n");
+ fprintf (stderr, "%s ", prefix);
+ vfprintf (stderr, fmt, ap);
+ fprintf (stderr, "\n%s\n", divider);
+#endif
+#if 0 && defined(WIN32)
+#error disabled because it is not working yet!
+ // wait for a keypress before quitting. Depending on how bochs is
+ // installed, the console window can disappear before the user has
+ // a chance to read the final message.
+ fprintf (stderr, "\n\nPress Enter to exit...\n");
+ char buf[8];
+ fgets (buf, 8, stdin);
+#endif
+#if !BX_DEBUGGER
+ BX_EXIT(exit_status);
+#else
+ static bx_bool dbg_exit_called = 0;
+ if (dbg_exit_called == 0) {
+ dbg_exit_called = 1;
+ bx_dbg_exit(exit_status);
+ }
+#endif
+ // not safe to use BX_* log functions in here.
+ fprintf (stderr, "fatal() should never return, but it just did\n");
+}
+
+iofunc_t *io = NULL;
+logfunc_t *genlog = NULL;
+
+void bx_center_print (FILE *file, char *line, int maxwidth)
+{
+ int imax;
+ int len = strlen(line);
+ if (len > maxwidth)
+ BX_PANIC (("bx_center_print: line is too long: '%s'", line));
+ imax = (maxwidth - len) >> 1;
+ for (int i=0; i<imax; i++) fputc (' ', file);
+ fputs (line, file);
+}
+
+
diff --git a/tools/ioemu/iodev/main.cc b/tools/ioemu/iodev/main.cc
new file mode 100644
index 0000000000..c784adb555
--- /dev/null
+++ b/tools/ioemu/iodev/main.cc
@@ -0,0 +1,4067 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: main.cc,v 1.256.2.3 2004/02/08 14:39:50 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+// Copyright (C) 2004 Arun Sharma <arun.sharma@intel.com>
+// Copyright (C) 2004 Intel Corp
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#include "bochs.h"
+#include <assert.h>
+#include "state_file.h"
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+extern "C" {
+#include <signal.h>
+}
+
+#if BX_GUI_SIGHANDLER
+bx_bool bx_gui_sighandler = 0;
+#endif
+
+// single processor simulation, so there's one of everything
+BOCHSAPI BX_CPU_C bx_cpu;
+BOCHSAPI BX_MEM_C bx_mem;
+
+int xc_handle;
+int bochsrc_include_count = 0;
+
+// some prototypes from iodev/
+// I want to stay away from including iodev/iodev.h here
+Bit32u bx_unmapped_io_read_handler(Bit32u address, unsigned io_len);
+void bx_unmapped_io_write_handler(Bit32u address, Bit32u value,
+ unsigned io_len);
+void bx_close_harddrive(void);
+
+
+void bx_init_bx_dbg (void);
+static char *divider = "========================================================================";
+static logfunctions thePluginLog;
+logfunctions *pluginlog = &thePluginLog;
+
+bx_startup_flags_t bx_startup_flags;
+bx_bool bx_user_quit;
+
+/* typedefs */
+
+#define LOG_THIS genlog->
+
+#if ( BX_PROVIDE_DEVICE_MODELS==1 )
+bx_pc_system_c bx_pc_system;
+class state_file state_stuff("state_file.out", "options");
+#endif
+
+bx_debug_t bx_dbg;
+
+bx_options_t bx_options; // initialized in bx_init_options()
+char *bochsrc_filename = NULL;
+
+static Bit32s parse_line_unformatted(char *context, char *line);
+static Bit32s parse_line_formatted(char *context, int num_params, char *params[]);
+static int parse_bochsrc(char *rcfile);
+
+static Bit64s
+bx_param_handler (bx_param_c *param, int set, Bit64s val)
+{
+ bx_id id = param->get_id ();
+ switch (id) {
+ case BXP_VGA_UPDATE_INTERVAL:
+ // if after init, notify the vga device to change its timer.
+ if (set && SIM->get_init_done ())
+ DEV_vga_set_update_interval (val);
+ break;
+ case BXP_MOUSE_ENABLED:
+ // if after init, notify the GUI
+ if (set && SIM->get_init_done ()) {
+ bx_gui->mouse_enabled_changed (val!=0);
+ DEV_mouse_enabled_changed (val!=0);
+ }
+ break;
+ case BXP_NE2K_PRESENT:
+ if (set) {
+ int enable = (val != 0);
+ SIM->get_param (BXP_NE2K_IOADDR)->set_enabled (enable);
+ SIM->get_param (BXP_NE2K_IRQ)->set_enabled (enable);
+ SIM->get_param (BXP_NE2K_MACADDR)->set_enabled (enable);
+ SIM->get_param (BXP_NE2K_ETHMOD)->set_enabled (enable);
+ SIM->get_param (BXP_NE2K_ETHDEV)->set_enabled (enable);
+ SIM->get_param (BXP_NE2K_SCRIPT)->set_enabled (enable);
+ }
+ break;
+ case BXP_LOAD32BITOS_WHICH:
+ if (set) {
+ int enable = (val != Load32bitOSNone);
+ SIM->get_param (BXP_LOAD32BITOS_PATH)->set_enabled (enable);
+ SIM->get_param (BXP_LOAD32BITOS_IOLOG)->set_enabled (enable);
+ SIM->get_param (BXP_LOAD32BITOS_INITRD)->set_enabled (enable);
+ }
+ break;
+ case BXP_ATA0_MASTER_STATUS:
+ case BXP_ATA0_SLAVE_STATUS:
+ case BXP_ATA1_MASTER_STATUS:
+ case BXP_ATA1_SLAVE_STATUS:
+ case BXP_ATA2_MASTER_STATUS:
+ case BXP_ATA2_SLAVE_STATUS:
+ case BXP_ATA3_MASTER_STATUS:
+ case BXP_ATA3_SLAVE_STATUS:
+ if ((set) && (SIM->get_init_done ())) {
+ Bit8u device = id - BXP_ATA0_MASTER_STATUS;
+ Bit32u handle = DEV_hd_get_device_handle (device/2, device%2);
+ DEV_hd_set_cd_media_status(handle, val == BX_INSERTED);
+ bx_gui->update_drive_status_buttons ();
+ }
+ break;
+ case BXP_FLOPPYA_TYPE:
+ if ((set) && (!SIM->get_init_done ())) {
+ bx_options.floppya.Odevtype->set (val);
+ }
+ break;
+ case BXP_FLOPPYA_STATUS:
+ if ((set) && (SIM->get_init_done ())) {
+ DEV_floppy_set_media_status(0, val == BX_INSERTED);
+ bx_gui->update_drive_status_buttons ();
+ }
+ break;
+ case BXP_FLOPPYB_TYPE:
+ if ((set) && (!SIM->get_init_done ())) {
+ bx_options.floppyb.Odevtype->set (val);
+ }
+ break;
+ case BXP_FLOPPYB_STATUS:
+ if ((set) && (SIM->get_init_done ())) {
+ DEV_floppy_set_media_status(1, val == BX_INSERTED);
+ bx_gui->update_drive_status_buttons ();
+ }
+ break;
+ case BXP_KBD_PASTE_DELAY:
+ if ((set) && (SIM->get_init_done ())) {
+ DEV_kbd_paste_delay_changed ();
+ }
+ break;
+
+ case BXP_ATA0_MASTER_MODE:
+ case BXP_ATA0_SLAVE_MODE:
+ case BXP_ATA1_MASTER_MODE:
+ case BXP_ATA1_SLAVE_MODE:
+ case BXP_ATA2_MASTER_MODE:
+ case BXP_ATA2_SLAVE_MODE:
+ case BXP_ATA3_MASTER_MODE:
+ case BXP_ATA3_SLAVE_MODE:
+ if (set) {
+ int device = id - BXP_ATA0_MASTER_MODE;
+ switch (val) {
+ case BX_ATA_MODE_UNDOABLE:
+ case BX_ATA_MODE_VOLATILE:
+ //case BX_ATA_MODE_Z_UNDOABLE:
+ //case BX_ATA_MODE_Z_VOLATILE:
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (1);
+ break;
+ default:
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (0);
+ }
+ }
+ break;
+
+ case BXP_ATA0_MASTER_TYPE:
+ case BXP_ATA0_SLAVE_TYPE:
+ case BXP_ATA1_MASTER_TYPE:
+ case BXP_ATA1_SLAVE_TYPE:
+ case BXP_ATA2_MASTER_TYPE:
+ case BXP_ATA2_SLAVE_TYPE:
+ case BXP_ATA3_MASTER_TYPE:
+ case BXP_ATA3_SLAVE_TYPE:
+ if (set) {
+ int device = id - BXP_ATA0_MASTER_TYPE;
+ switch (val) {
+ case BX_ATA_DEVICE_DISK:
+ SIM->get_param_num ((bx_id)(BXP_ATA0_MASTER_PRESENT + device))->set (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODE + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_enabled (1);
+ //SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_CYLINDERS + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_HEADS + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_SPT + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_enabled (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODEL + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_BIOSDETECT + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_TRANSLATION + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_runtime_param (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_runtime_param (0);
+ break;
+ case BX_ATA_DEVICE_CDROM:
+ SIM->get_param_num ((bx_id)(BXP_ATA0_MASTER_PRESENT + device))->set (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODE + device))->set_enabled (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_JOURNAL + device))->set_enabled (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_CYLINDERS + device))->set_enabled (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_HEADS + device))->set_enabled (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_SPT + device))->set_enabled (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_MODEL + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_BIOSDETECT + device))->set_enabled (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_TRANSLATION + device))->set_enabled (0);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_PATH + device))->set_runtime_param (1);
+ SIM->get_param ((bx_id)(BXP_ATA0_MASTER_STATUS + device))->set_runtime_param (1);
+ break;
+ }
+ }
+ break;
+ default:
+ BX_PANIC (("bx_param_handler called with unknown id %d", id));
+ return -1;
+ }
+ return val;
+}
+
+char *bx_param_string_handler (bx_param_string_c *param, int set, char *val, int maxlen)
+{
+ bx_id id = param->get_id ();
+
+ int empty = 0;
+ if ((strlen(val) < 1) || !strcmp ("none", val)) {
+ empty = 1;
+ val = "none";
+ }
+ switch (id) {
+ case BXP_FLOPPYA_PATH:
+ if (set==1) {
+ if (SIM->get_init_done ()) {
+ if (empty) {
+ DEV_floppy_set_media_status(0, 0);
+ bx_gui->update_drive_status_buttons ();
+ } else {
+ if (!SIM->get_param_num(BXP_FLOPPYA_TYPE)->get_enabled()) {
+ BX_ERROR(("Cannot add a floppy drive at runtime"));
+ bx_options.floppya.Opath->set ("none");
+ }
+ }
+ if ((DEV_floppy_present()) &&
+ (SIM->get_param_num(BXP_FLOPPYA_STATUS)->get () == BX_INSERTED)) {
+ // tell the device model that we removed, then inserted the disk
+ DEV_floppy_set_media_status(0, 0);
+ DEV_floppy_set_media_status(0, 1);
+ }
+ } else {
+ SIM->get_param_num(BXP_FLOPPYA_DEVTYPE)->set_enabled (!empty);
+ SIM->get_param_num(BXP_FLOPPYA_TYPE)->set_enabled (!empty);
+ SIM->get_param_num(BXP_FLOPPYA_STATUS)->set_enabled (!empty);
+ }
+ }
+ break;
+ case BXP_FLOPPYB_PATH:
+ if (set==1) {
+ if (SIM->get_init_done ()) {
+ if (empty) {
+ DEV_floppy_set_media_status(1, 0);
+ bx_gui->update_drive_status_buttons ();
+ } else {
+ if (!SIM->get_param_num(BXP_FLOPPYB_TYPE)->get_enabled ()) {
+ BX_ERROR(("Cannot add a floppy drive at runtime"));
+ bx_options.floppyb.Opath->set ("none");
+ }
+ }
+ if ((DEV_floppy_present()) &&
+ (SIM->get_param_num(BXP_FLOPPYB_STATUS)->get () == BX_INSERTED)) {
+ // tell the device model that we removed, then inserted the disk
+ DEV_floppy_set_media_status(1, 0);
+ DEV_floppy_set_media_status(1, 1);
+ }
+ } else {
+ SIM->get_param_num(BXP_FLOPPYB_DEVTYPE)->set_enabled (!empty);
+ SIM->get_param_num(BXP_FLOPPYB_TYPE)->set_enabled (!empty);
+ SIM->get_param_num(BXP_FLOPPYB_STATUS)->set_enabled (!empty);
+ }
+ }
+ break;
+
+ case BXP_ATA0_MASTER_PATH:
+ case BXP_ATA0_SLAVE_PATH:
+ case BXP_ATA1_MASTER_PATH:
+ case BXP_ATA1_SLAVE_PATH:
+ case BXP_ATA2_MASTER_PATH:
+ case BXP_ATA2_SLAVE_PATH:
+ case BXP_ATA3_MASTER_PATH:
+ case BXP_ATA3_SLAVE_PATH:
+ if (set==1) {
+ if (SIM->get_init_done ()) {
+
+ Bit8u device = id - BXP_ATA0_MASTER_PATH;
+ Bit32u handle = DEV_hd_get_device_handle(device/2, device%2);
+
+ if (empty) {
+ DEV_hd_set_cd_media_status(handle, 0);
+ bx_gui->update_drive_status_buttons ();
+ } else {
+ if (!SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_PRESENT + device))->get ()) {
+ BX_ERROR(("Cannot add a cdrom drive at runtime"));
+ bx_options.atadevice[device/2][device%2].Opresent->set (0);
+ }
+ if (SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get () != BX_ATA_DEVICE_CDROM) {
+ BX_ERROR(("Device is not a cdrom drive"));
+ bx_options.atadevice[device/2][device%2].Opresent->set (0);
+ }
+ }
+ if (DEV_hd_present() &&
+ (SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_STATUS + device))->get () == BX_INSERTED) &&
+ (SIM->get_param_num((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get () == BX_ATA_DEVICE_CDROM)) {
+ // tell the device model that we removed, then inserted the cd
+ DEV_hd_set_cd_media_status(handle, 0);
+ DEV_hd_set_cd_media_status(handle, 1);
+ }
+ }
+ }
+ break;
+
+ case BXP_SCREENMODE:
+ if (set==1) {
+ BX_INFO (("Screen mode changed to %s", val));
+ }
+ break;
+ default:
+ BX_PANIC (("bx_string_handler called with unexpected parameter %d", param->get_id()));
+ }
+ return val;
+}
+
+static int
+bx_param_enable_handler (bx_param_c *param, int val)
+{
+ bx_id id = param->get_id ();
+ switch (id) {
+ case BXP_ATA0_MASTER_STATUS:
+ case BXP_ATA0_SLAVE_STATUS:
+ case BXP_ATA1_MASTER_STATUS:
+ case BXP_ATA1_SLAVE_STATUS:
+ case BXP_ATA2_MASTER_STATUS:
+ case BXP_ATA2_SLAVE_STATUS:
+ case BXP_ATA3_MASTER_STATUS:
+ case BXP_ATA3_SLAVE_STATUS:
+ if (val != 0) {
+ Bit8u device = id - BXP_ATA0_MASTER_STATUS;
+
+ switch (SIM->get_param_enum ((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get()) {
+ case BX_ATA_DEVICE_CDROM:
+ return (1);
+ break;
+ }
+ }
+ return (0);
+ break;
+
+ case BXP_ATA0_MASTER_JOURNAL:
+ case BXP_ATA0_SLAVE_JOURNAL:
+ case BXP_ATA1_MASTER_JOURNAL:
+ case BXP_ATA1_SLAVE_JOURNAL:
+ case BXP_ATA2_MASTER_JOURNAL:
+ case BXP_ATA2_SLAVE_JOURNAL:
+ case BXP_ATA3_MASTER_JOURNAL:
+ case BXP_ATA3_SLAVE_JOURNAL:
+ if (val != 0) {
+ Bit8u device = id - BXP_ATA0_MASTER_JOURNAL;
+
+ switch (SIM->get_param_enum ((bx_id)(BXP_ATA0_MASTER_TYPE + device))->get()) {
+ case BX_ATA_DEVICE_DISK:
+ switch (SIM->get_param_enum ((bx_id)(BXP_ATA0_MASTER_MODE + device))->get()) {
+ case BX_ATA_MODE_UNDOABLE:
+ case BX_ATA_MODE_VOLATILE:
+ //case BX_ATA_MODE_Z_UNDOABLE:
+ //case BX_ATA_MODE_Z_VOLATILE:
+ return (1);
+ break;
+ }
+ }
+ }
+ return (0);
+ break;
+
+ default:
+ BX_PANIC (("bx_param_handler called with unknown id %d", id));
+ }
+ return val;
+}
+
+
+
+void bx_init_options ()
+{
+ int i;
+ bx_list_c *menu;
+ bx_list_c *deplist;
+ char name[1024], descr[1024], label[1024];
+
+ memset (&bx_options, 0, sizeof(bx_options));
+
+ // quick start option, set by command line arg
+ new bx_param_enum_c (BXP_BOCHS_START,
+ "Bochs start types",
+ "Bochs start types",
+ bochs_start_names,
+ BX_RUN_START,
+ BX_QUICK_START);
+
+ // floppya
+ bx_options.floppya.Opath = new bx_param_filename_c (BXP_FLOPPYA_PATH,
+ "floppya:path",
+ "Pathname of first floppy image file or device. If you're booting from floppy, this should be a bootable floppy.",
+ "", BX_PATHNAME_LEN);
+ bx_options.floppya.Opath->set_ask_format ("Enter new filename, or 'none' for no disk: [%s] ");
+ bx_options.floppya.Opath->set_label ("First floppy image/device");
+ bx_options.floppya.Odevtype = new bx_param_enum_c (BXP_FLOPPYA_DEVTYPE,
+ "floppya:devtype",
+ "Type of floppy drive",
+ floppy_type_names,
+ BX_FLOPPY_NONE,
+ BX_FLOPPY_NONE);
+ bx_options.floppya.Otype = new bx_param_enum_c (BXP_FLOPPYA_TYPE,
+ "floppya:type",
+ "Type of floppy disk",
+ floppy_type_names,
+ BX_FLOPPY_NONE,
+ BX_FLOPPY_NONE);
+ bx_options.floppya.Otype->set_ask_format ("What type of floppy disk? [%s] ");
+ bx_options.floppya.Ostatus = new bx_param_enum_c (BXP_FLOPPYA_STATUS,
+ "Is floppya inserted",
+ "Inserted or ejected",
+ floppy_status_names,
+ BX_INSERTED,
+ BX_EJECTED);
+ bx_options.floppya.Ostatus->set_ask_format ("Is the floppy inserted or ejected? [%s] ");
+ bx_options.floppya.Opath->set_format ("%s");
+ bx_options.floppya.Otype->set_format ("size=%s");
+ bx_options.floppya.Ostatus->set_format ("%s");
+ bx_param_c *floppya_init_list[] = {
+ // if the order "path,type,status" changes, corresponding changes must
+ // be made in gui/wxmain.cc, MyFrame::editFloppyConfig.
+ bx_options.floppya.Opath,
+ bx_options.floppya.Otype,
+ bx_options.floppya.Ostatus,
+ NULL
+ };
+ menu = new bx_list_c (BXP_FLOPPYA, "Floppy Disk 0", "All options for first floppy disk", floppya_init_list);
+ menu->get_options ()->set (menu->SERIES_ASK);
+ bx_options.floppya.Opath->set_handler (bx_param_string_handler);
+ bx_options.floppya.Opath->set ("none");
+ bx_options.floppya.Otype->set_handler (bx_param_handler);
+ bx_options.floppya.Ostatus->set_handler (bx_param_handler);
+
+ bx_options.floppyb.Opath = new bx_param_filename_c (BXP_FLOPPYB_PATH,
+ "floppyb:path",
+ "Pathname of second floppy image file or device.",
+ "", BX_PATHNAME_LEN);
+ bx_options.floppyb.Opath->set_ask_format ("Enter new filename, or 'none' for no disk: [%s] ");
+ bx_options.floppyb.Opath->set_label ("Second floppy image/device");
+ bx_options.floppyb.Odevtype = new bx_param_enum_c (BXP_FLOPPYB_DEVTYPE,
+ "floppyb:devtype",
+ "Type of floppy drive",
+ floppy_type_names,
+ BX_FLOPPY_NONE,
+ BX_FLOPPY_NONE);
+ bx_options.floppyb.Otype = new bx_param_enum_c (BXP_FLOPPYB_TYPE,
+ "floppyb:type",
+ "Type of floppy disk",
+ floppy_type_names,
+ BX_FLOPPY_NONE,
+ BX_FLOPPY_NONE);
+ bx_options.floppyb.Otype->set_ask_format ("What type of floppy disk? [%s] ");
+ bx_options.floppyb.Ostatus = new bx_param_enum_c (BXP_FLOPPYB_STATUS,
+ "Is floppyb inserted",
+ "Inserted or ejected",
+ floppy_status_names,
+ BX_INSERTED,
+ BX_EJECTED);
+ bx_options.floppyb.Ostatus->set_ask_format ("Is the floppy inserted or ejected? [%s] ");
+ bx_options.floppyb.Opath->set_format ("%s");
+ bx_options.floppyb.Otype->set_format ("size=%s");
+ bx_options.floppyb.Ostatus->set_format ("%s");
+ bx_param_c *floppyb_init_list[] = {
+ bx_options.floppyb.Opath,
+ bx_options.floppyb.Otype,
+ bx_options.floppyb.Ostatus,
+ NULL
+ };
+ menu = new bx_list_c (BXP_FLOPPYB, "Floppy Disk 1", "All options for second floppy disk", floppyb_init_list);
+ menu->get_options ()->set (menu->SERIES_ASK);
+ bx_options.floppyb.Opath->set_handler (bx_param_string_handler);
+ bx_options.floppyb.Opath->set ("none");
+ bx_options.floppyb.Otype->set_handler (bx_param_handler);
+ bx_options.floppyb.Ostatus->set_handler (bx_param_handler);
+
+ // disk options
+
+ // FIXME use descr and name
+ char *s_atachannel[] = {
+ "ATA channel 0",
+ "ATA channel 1",
+ "ATA channel 2",
+ "ATA channel 3",
+ };
+ char *s_atadevice[4][2] = {
+ { "First HD/CD on channel 0",
+ "Second HD/CD on channel 0" },
+ { "First HD/CD on channel 1",
+ "Second HD/CD on channel 1" },
+ { "First HD/CD on channel 2",
+ "Second HD/CD on channel 2" },
+ { "First HD/CD on channel 3",
+ "Second HD/CD on channel 3" }
+ };
+ Bit16u ata_default_ioaddr1[BX_MAX_ATA_CHANNEL] = {
+ 0x1f0, 0x170, 0x1e8, 0x168
+ };
+ Bit16u ata_default_ioaddr2[BX_MAX_ATA_CHANNEL] = {
+ 0x3f0, 0x370, 0x3e0, 0x360
+ };
+ Bit8u ata_default_irq[BX_MAX_ATA_CHANNEL] = {
+ 14, 15, 11, 9
+ };
+
+ bx_list_c *ata[BX_MAX_ATA_CHANNEL];
+ bx_list_c *ata_menu[BX_MAX_ATA_CHANNEL];
+
+ Bit8u channel;
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel ++) {
+
+ ata[channel] = new bx_list_c ((bx_id)(BXP_ATAx(channel)), s_atachannel[channel], s_atachannel[channel], 8);
+ ata[channel]->get_options ()->set (bx_list_c::SERIES_ASK);
+
+ ata[channel]->add (bx_options.ata[channel].Opresent = new bx_param_bool_c ((bx_id)(BXP_ATAx_PRESENT(channel)),
+ "ata:present",
+ "Controls whether ata channel is installed or not",
+ 0));
+
+ ata[channel]->add (bx_options.ata[channel].Oioaddr1 = new bx_param_num_c ((bx_id)(BXP_ATAx_IOADDR1(channel)),
+ "ata:ioaddr1",
+ "IO adress of ata command block",
+ 0, 0xffff,
+ ata_default_ioaddr1[channel]));
+
+ ata[channel]->add (bx_options.ata[channel].Oioaddr2 = new bx_param_num_c ((bx_id)(BXP_ATAx_IOADDR2(channel)),
+ "ata:ioaddr2",
+ "IO adress of ata control block",
+ 0, 0xffff,
+ ata_default_ioaddr2[channel]));
+
+ ata[channel]->add (bx_options.ata[channel].Oirq = new bx_param_num_c ((bx_id)(BXP_ATAx_IRQ(channel)),
+ "ata:irq",
+ "IRQ used by this ata channel",
+ 0, 15,
+ ata_default_irq[channel]));
+
+ // all items in the ata[channel] menu depend on the present flag.
+ // The menu list is complete, but a few dependent_list items will
+ // be added later. Use clone() to make a copy of the dependent_list
+ // so that it can be changed without affecting the menu.
+ bx_options.ata[channel].Opresent->set_dependent_list (
+ ata[channel]->clone());
+
+ for (Bit8u slave=0; slave<2; slave++) {
+
+ menu = bx_options.atadevice[channel][slave].Omenu = new bx_list_c ((bx_id)(BXP_ATAx_DEVICE(channel,slave)),
+ s_atadevice[channel][slave],
+ s_atadevice[channel][slave],
+ BXP_PARAMS_PER_ATA_DEVICE + 1 );
+ menu->get_options ()->set (menu->SERIES_ASK);
+
+ menu->add (bx_options.atadevice[channel][slave].Opresent = new bx_param_bool_c ((bx_id)(BXP_ATAx_DEVICE_PRESENT(channel,slave)),
+ "ata-device:present",
+ "Controls whether ata device is installed or not",
+ 0));
+
+ menu->add (bx_options.atadevice[channel][slave].Otype = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_TYPE(channel,slave)),
+ "ata-device:type",
+ "Type of ATA device (disk or cdrom)",
+ atadevice_type_names,
+ BX_ATA_DEVICE_DISK,
+ BX_ATA_DEVICE_DISK));
+
+ menu->add (bx_options.atadevice[channel][slave].Opath = new bx_param_filename_c ((bx_id)(BXP_ATAx_DEVICE_PATH(channel,slave)),
+ "ata-device:path",
+ "Pathname of the image or physical device (cdrom only)",
+ "", BX_PATHNAME_LEN));
+
+ menu->add (bx_options.atadevice[channel][slave].Omode = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_MODE(channel,slave)),
+ "ata-device:mode",
+ "Mode of the ATA harddisk",
+ atadevice_mode_names,
+ BX_ATA_MODE_FLAT,
+ BX_ATA_MODE_FLAT));
+
+ menu->add (bx_options.atadevice[channel][slave].Ostatus = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_STATUS(channel,slave)),
+ "ata-device:status",
+ "CD-ROM media status (inserted / ejected)",
+ atadevice_status_names,
+ BX_INSERTED,
+ BX_EJECTED));
+
+ menu->add (bx_options.atadevice[channel][slave].Ojournal = new bx_param_filename_c ((bx_id)(BXP_ATAx_DEVICE_JOURNAL(channel,slave)),
+ "ata-device:journal",
+ "Pathname of the journal file",
+ "", BX_PATHNAME_LEN));
+
+ menu->add (bx_options.atadevice[channel][slave].Ocylinders = new bx_param_num_c ((bx_id)(BXP_ATAx_DEVICE_CYLINDERS(channel,slave)),
+ "ata-device:cylinders",
+ "Number of cylinders",
+ 0, 65535,
+ 0));
+ menu->add (bx_options.atadevice[channel][slave].Oheads = new bx_param_num_c ((bx_id)(BXP_ATAx_DEVICE_HEADS(channel,slave)),
+ "ata-device:heads",
+ "Number of heads",
+ 0, 65535,
+ 0));
+ menu->add (bx_options.atadevice[channel][slave].Ospt = new bx_param_num_c ((bx_id)(BXP_ATAx_DEVICE_SPT(channel,slave)),
+ "ata-device:spt",
+ "Number of sectors per track",
+ 0, 65535,
+ 0));
+
+ menu->add (bx_options.atadevice[channel][slave].Omodel = new bx_param_string_c ((bx_id)(BXP_ATAx_DEVICE_MODEL(channel,slave)),
+ "ata-device:model",
+ "String returned by the 'identify device' command",
+ "Generic 1234", 40));
+
+ menu->add (bx_options.atadevice[channel][slave].Obiosdetect = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_BIOSDETECT(channel,slave)),
+ "ata-device:biosdetect",
+ "Type of bios detection",
+ atadevice_biosdetect_names,
+ BX_ATA_BIOSDETECT_AUTO,
+ BX_ATA_BIOSDETECT_NONE));
+
+ menu->add (bx_options.atadevice[channel][slave].Otranslation = new bx_param_enum_c ((bx_id)(BXP_ATAx_DEVICE_TRANSLATION(channel,slave)),
+ "How the ata-disk translation is done by the bios",
+ "Type of translation",
+ atadevice_translation_names,
+ BX_ATA_TRANSLATION_AUTO,
+ BX_ATA_TRANSLATION_NONE));
+
+ bx_options.atadevice[channel][slave].Opresent->set_dependent_list (
+ menu->clone ());
+ // the menu and all items on it depend on the Opresent flag
+ bx_options.atadevice[channel][slave].Opresent->get_dependent_list()->add(menu);
+ // the present flag depends on the ATA channel's present flag
+ bx_options.ata[channel].Opresent->get_dependent_list()->add (
+ bx_options.atadevice[channel][slave].Opresent);
+ }
+
+ // set up top level menu for ATA[i] controller configuration. This list
+ // controls what will appear on the ATA configure dialog. It now
+ // requests the USE_TAB_WINDOW display, which is implemented in wx.
+ char buffer[32];
+ sprintf (buffer, "Configure ATA%d", channel);
+ ata_menu[channel] = new bx_list_c ((bx_id)(BXP_ATAx_MENU(channel)), strdup(buffer), "", 4);
+ ata_menu[channel]->add (ata[channel]);
+ ata_menu[channel]->add (bx_options.atadevice[channel][0].Omenu);
+ ata_menu[channel]->add (bx_options.atadevice[channel][1].Omenu);
+ ata_menu[channel]->get_options()->set (bx_list_c::USE_TAB_WINDOW);
+ }
+
+ // Enable first ata interface by default, disable the others.
+ bx_options.ata[0].Opresent->set_initial_val(1);
+
+ // now that the dependence relationships are established, call set() on
+ // the ata device present params to set all enables correctly.
+ for (i=0; i<BX_MAX_ATA_CHANNEL; i++)
+ bx_options.ata[i].Opresent->set (i==0);
+
+ for (channel=0; channel<BX_MAX_ATA_CHANNEL; channel ++) {
+
+ bx_options.ata[channel].Opresent->set_ask_format ("Channel is enabled: [%s] ");
+ bx_options.ata[channel].Oioaddr1->set_ask_format ("Enter new ioaddr1: [0x%x] ");
+ bx_options.ata[channel].Oioaddr2->set_ask_format ("Enter new ioaddr2: [0x%x] ");
+ bx_options.ata[channel].Oirq->set_ask_format ("Enter new IRQ: [%d] ");
+#if BX_WITH_WX
+ bx_options.ata[channel].Opresent->set_label ("Enable this channel?");
+ bx_options.ata[channel].Oioaddr1->set_label ("I/O Address 1:");
+ bx_options.ata[channel].Oioaddr2->set_label ("I/O Address 2:");
+ bx_options.ata[channel].Oirq->set_label ("IRQ:");
+ bx_options.ata[channel].Oirq->set_options (bx_param_num_c::USE_SPIN_CONTROL);
+#else
+ bx_options.ata[channel].Opresent->set_format ("enabled: %s");
+ bx_options.ata[channel].Oioaddr1->set_format ("ioaddr1: 0x%x");
+ bx_options.ata[channel].Oioaddr2->set_format ("ioaddr2: 0x%x");
+ bx_options.ata[channel].Oirq->set_format ("irq: %d");
+#endif
+ bx_options.ata[channel].Oioaddr1->set_base (16);
+ bx_options.ata[channel].Oioaddr2->set_base (16);
+
+ for (Bit8u slave=0; slave<2; slave++) {
+
+ bx_options.atadevice[channel][slave].Opresent->set_ask_format (
+ "Device is enabled: [%s] ");
+ bx_options.atadevice[channel][slave].Otype->set_ask_format (
+ "Enter type of ATA device, disk or cdrom: [%s] ");
+ bx_options.atadevice[channel][slave].Omode->set_ask_format (
+ "Enter mode of ATA device, (flat, concat, etc.): [%s] ");
+ bx_options.atadevice[channel][slave].Opath->set_ask_format (
+ "Enter new filename: [%s] ");
+ bx_options.atadevice[channel][slave].Ocylinders->set_ask_format (
+ "Enter number of cylinders: [%d] ");
+ bx_options.atadevice[channel][slave].Oheads->set_ask_format (
+ "Enter number of heads: [%d] ");
+ bx_options.atadevice[channel][slave].Ospt->set_ask_format (
+ "Enter number of sectors per track: [%d] ");
+ bx_options.atadevice[channel][slave].Ostatus->set_ask_format (
+ "Is the device inserted or ejected? [%s] ");
+ bx_options.atadevice[channel][slave].Omodel->set_ask_format (
+ "Enter new model name: [%s]");
+ bx_options.atadevice[channel][slave].Otranslation->set_ask_format (
+ "Enter translation type: [%s]");
+ bx_options.atadevice[channel][slave].Obiosdetect->set_ask_format (
+ "Enter bios detection type: [%s]");
+ bx_options.atadevice[channel][slave].Ojournal->set_ask_format (
+ "Enter path of journal file: [%s]");
+
+#if BX_WITH_WX
+ bx_options.atadevice[channel][slave].Opresent->set_label (
+ "Enable this device?");
+ bx_options.atadevice[channel][slave].Otype->set_label (
+ "Type of ATA device:");
+ bx_options.atadevice[channel][slave].Omode->set_label (
+ "Type of disk image:");
+ bx_options.atadevice[channel][slave].Opath->set_label (
+ "Path or physical device name:");
+ bx_options.atadevice[channel][slave].Ocylinders->set_label (
+ "Cylinders:");
+ bx_options.atadevice[channel][slave].Oheads->set_label (
+ "Heads:");
+ bx_options.atadevice[channel][slave].Ospt->set_label (
+ "Sectors per track:");
+ bx_options.atadevice[channel][slave].Ostatus->set_label (
+ "Inserted?");
+ bx_options.atadevice[channel][slave].Omodel->set_label (
+ "Model name:");
+ bx_options.atadevice[channel][slave].Otranslation->set_label (
+ "Translation type:");
+ bx_options.atadevice[channel][slave].Obiosdetect->set_label (
+ "BIOS Detection:");
+ bx_options.atadevice[channel][slave].Ojournal->set_label (
+ "Path of journal file:");
+#else
+ bx_options.atadevice[channel][slave].Opresent->set_format ("enabled: %s");
+ bx_options.atadevice[channel][slave].Otype->set_format ("type %s");
+ bx_options.atadevice[channel][slave].Omode->set_format ("mode %s");
+ bx_options.atadevice[channel][slave].Opath->set_format ("path '%s'");
+ bx_options.atadevice[channel][slave].Ocylinders->set_format ("%d cylinders");
+ bx_options.atadevice[channel][slave].Oheads->set_format ("%d heads");
+ bx_options.atadevice[channel][slave].Ospt->set_format ("%d sectors/track");
+ bx_options.atadevice[channel][slave].Ostatus->set_format ("%s");
+ bx_options.atadevice[channel][slave].Omodel->set_format ("model '%s'");
+ bx_options.atadevice[channel][slave].Otranslation->set_format ("translation '%s'");
+ bx_options.atadevice[channel][slave].Obiosdetect->set_format ("biosdetect '%s'");
+ bx_options.atadevice[channel][slave].Ojournal->set_format ("journal is '%s'");
+#endif
+
+ bx_options.atadevice[channel][slave].Otype->set_handler (bx_param_handler);
+ bx_options.atadevice[channel][slave].Omode->set_handler (bx_param_handler);
+ bx_options.atadevice[channel][slave].Ostatus->set_handler (bx_param_handler);
+ bx_options.atadevice[channel][slave].Opath->set_handler (bx_param_string_handler);
+
+ // Set the enable_hanlders
+ bx_options.atadevice[channel][slave].Ojournal->set_enable_handler (bx_param_enable_handler);
+ bx_options.atadevice[channel][slave].Ostatus->set_enable_handler (bx_param_enable_handler);
+
+ }
+ }
+
+ bx_options.OnewHardDriveSupport = new bx_param_bool_c (BXP_NEWHARDDRIVESUPPORT,
+ "New hard drive support",
+ "Enables new features found on newer hard drives.",
+ 1);
+
+ bx_options.Obootdrive = new bx_param_enum_c (BXP_BOOTDRIVE,
+ "bootdrive",
+ "Boot A, C or CD",
+ floppy_bootdisk_names,
+ BX_BOOT_FLOPPYA,
+ BX_BOOT_FLOPPYA);
+ bx_options.Obootdrive->set_format ("Boot from: %s drive");
+ bx_options.Obootdrive->set_ask_format ("Boot from floppy drive, hard drive or cdrom ? [%s] ");
+
+ bx_options.OfloppySigCheck = new bx_param_bool_c (BXP_FLOPPYSIGCHECK,
+ "Skip Floppy Boot Signature Check",
+ "Skips check for the 0xaa55 signature on floppy boot device.",
+ 0);
+
+ // disk menu
+ bx_param_c *disk_menu_init_list[] = {
+ SIM->get_param (BXP_FLOPPYA),
+ SIM->get_param (BXP_FLOPPYB),
+ SIM->get_param (BXP_ATA0),
+ SIM->get_param (BXP_ATA0_MASTER),
+ SIM->get_param (BXP_ATA0_SLAVE),
+#if BX_MAX_ATA_CHANNEL>1
+ SIM->get_param (BXP_ATA1),
+ SIM->get_param (BXP_ATA1_MASTER),
+ SIM->get_param (BXP_ATA1_SLAVE),
+#endif
+#if BX_MAX_ATA_CHANNEL>2
+ SIM->get_param (BXP_ATA2),
+ SIM->get_param (BXP_ATA2_MASTER),
+ SIM->get_param (BXP_ATA2_SLAVE),
+#endif
+#if BX_MAX_ATA_CHANNEL>3
+ SIM->get_param (BXP_ATA3),
+ SIM->get_param (BXP_ATA3_MASTER),
+ SIM->get_param (BXP_ATA3_SLAVE),
+#endif
+ SIM->get_param (BXP_NEWHARDDRIVESUPPORT),
+ SIM->get_param (BXP_BOOTDRIVE),
+ SIM->get_param (BXP_FLOPPYSIGCHECK),
+ NULL
+ };
+ menu = new bx_list_c (BXP_MENU_DISK, "Bochs Disk Options", "diskmenu", disk_menu_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+
+ // memory options menu
+ bx_options.memory.Osize = new bx_param_num_c (BXP_MEM_SIZE,
+ "megs",
+ "Amount of RAM in megabytes",
+ 1, BX_MAX_BIT32U,
+ BX_DEFAULT_MEM_MEGS);
+ bx_options.memory.Osize->set_ask_format ("Enter memory size (MB): [%d] ");
+#if BX_WITH_WX
+ bx_options.memory.Osize->set_label ("Memory size (megabytes)");
+ bx_options.memory.Osize->set_options (bx_param_num_c::USE_SPIN_CONTROL);
+#else
+ bx_options.memory.Osize->set_format ("Memory size in megabytes: %d");
+#endif
+
+ // initialize serial and parallel port options
+#define PAR_SER_INIT_LIST_MAX \
+ ((BXP_PARAMS_PER_PARALLEL_PORT * BX_N_PARALLEL_PORTS) \
+ + (BXP_PARAMS_PER_SERIAL_PORT * BX_N_SERIAL_PORTS) \
+ + (BXP_PARAMS_PER_USB_HUB * BX_N_USB_HUBS))
+ bx_param_c *par_ser_init_list[1+PAR_SER_INIT_LIST_MAX];
+ bx_param_c **par_ser_ptr = &par_ser_init_list[0];
+
+ // parallel ports
+ for (i=0; i<BX_N_PARALLEL_PORTS; i++) {
+ sprintf (name, "Enable parallel port #%d", i+1);
+ sprintf (descr, "Controls whether parallel port #%d is installed or not", i+1);
+ bx_options.par[i].Oenabled = new bx_param_bool_c (
+ BXP_PARPORTx_ENABLED(i+1),
+ strdup(name),
+ strdup(descr),
+ (i==0)? 1 : 0); // only enable #1 by default
+ sprintf (name, "Parallel port #%d output file", i+1);
+ sprintf (descr, "Data written to parport#%d by the guest OS is written to this file", i+1);
+ bx_options.par[i].Ooutfile = new bx_param_filename_c (
+ BXP_PARPORTx_OUTFILE(i+1),
+ strdup(name),
+ strdup(descr),
+ "", BX_PATHNAME_LEN);
+ deplist = new bx_list_c (BXP_NULL, 1);
+ deplist->add (bx_options.par[i].Ooutfile);
+ bx_options.par[i].Oenabled->set_dependent_list (deplist);
+ // add to menu
+ *par_ser_ptr++ = bx_options.par[i].Oenabled;
+ *par_ser_ptr++ = bx_options.par[i].Ooutfile;
+ }
+
+ // serial ports
+ for (i=0; i<BX_N_SERIAL_PORTS; i++) {
+ // options for COM port
+ sprintf (name, "Enable serial port #%d (COM%d)", i+1, i+1);
+ sprintf (descr, "Controls whether COM%d is installed or not", i+1);
+ bx_options.com[i].Oenabled = new bx_param_bool_c (
+ BXP_COMx_ENABLED(i+1),
+ strdup(name),
+ strdup(descr),
+ (i==0)?1 : 0); // only enable the first by default
+ sprintf (name, "Pathname of the serial device for COM%d", i+1);
+ sprintf (descr, "The path can be a real serial device or a pty (X/Unix only)");
+ bx_options.com[i].Odev = new bx_param_filename_c (
+ BXP_COMx_PATH(i+1),
+ strdup(name),
+ strdup(descr),
+ "", BX_PATHNAME_LEN);
+ deplist = new bx_list_c (BXP_NULL, 1);
+ deplist->add (bx_options.com[i].Odev);
+ bx_options.com[i].Oenabled->set_dependent_list (deplist);
+ // add to menu
+ *par_ser_ptr++ = bx_options.com[i].Oenabled;
+ *par_ser_ptr++ = bx_options.com[i].Odev;
+ }
+
+ // usb hubs
+ for (i=0; i<BX_N_USB_HUBS; i++) {
+ // options for USB hub
+ sprintf (name, "usb%d:enabled", i+1);
+ sprintf (descr, "Controls whether USB%d is installed or not", i+1);
+ sprintf (label, "Enable usb hub #%d (USB%d)", i+1, i+1);
+ bx_options.usb[i].Oenabled = new bx_param_bool_c (
+ BXP_USBx_ENABLED(i+1),
+ strdup(name),
+ strdup(descr),
+ (i==0)?1 : 0); // only enable the first by default
+ bx_options.usb[i].Oioaddr = new bx_param_num_c (
+ BXP_USBx_IOADDR(i+1),
+ "usb:ioaddr",
+ "I/O base adress of USB hub",
+ 0, 0xffe0,
+ (i==0)?0xff80 : 0);
+ bx_options.usb[i].Oirq = new bx_param_num_c (
+ BXP_USBx_IRQ(i+1),
+ "usb:irq",
+ "IRQ used by USB hub",
+ 0, 15,
+ (i==0)?10 : 0);
+ deplist = new bx_list_c (BXP_NULL, 2);
+ deplist->add (bx_options.usb[i].Oioaddr);
+ deplist->add (bx_options.usb[i].Oirq);
+ bx_options.usb[i].Oenabled->set_dependent_list (deplist);
+ // add to menu
+ *par_ser_ptr++ = bx_options.usb[i].Oenabled;
+ *par_ser_ptr++ = bx_options.usb[i].Oioaddr;
+ *par_ser_ptr++ = bx_options.usb[i].Oirq;
+
+ bx_options.usb[i].Oioaddr->set_ask_format ("Enter new ioaddr: [0x%x] ");
+ bx_options.usb[i].Oioaddr->set_label ("I/O Address");
+ bx_options.usb[i].Oioaddr->set_base (16);
+ bx_options.usb[i].Oenabled->set_label (strdup(label));
+ bx_options.usb[i].Oirq->set_label ("USB IRQ");
+ bx_options.usb[i].Oirq->set_options (bx_param_num_c::USE_SPIN_CONTROL);
+ }
+ // add final NULL at the end, and build the menu
+ *par_ser_ptr = NULL;
+ menu = new bx_list_c (BXP_MENU_SERIAL_PARALLEL,
+ "Serial and Parallel Port Options",
+ "serial_parallel_menu",
+ par_ser_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+
+ bx_options.rom.Opath = new bx_param_filename_c (BXP_ROM_PATH,
+ "romimage",
+ "Pathname of ROM image to load",
+ "", BX_PATHNAME_LEN);
+ bx_options.rom.Opath->set_format ("Name of ROM BIOS image: %s");
+ bx_options.rom.Oaddress = new bx_param_num_c (BXP_ROM_ADDRESS,
+ "romaddr",
+ "The address at which the ROM image should be loaded",
+ 0, BX_MAX_BIT32U,
+ 0xf0000);
+ bx_options.rom.Oaddress->set_base (16);
+#if BX_WITH_WX
+ bx_options.rom.Opath->set_label ("ROM BIOS image");
+ bx_options.rom.Oaddress->set_label ("ROM BIOS address");
+#else
+ bx_options.rom.Oaddress->set_format ("ROM BIOS address: 0x%05x");
+#endif
+
+ bx_options.optrom[0].Opath = new bx_param_filename_c (BXP_OPTROM1_PATH,
+ "optional romimage #1",
+ "Pathname of optional ROM image #1 to load",
+ "", BX_PATHNAME_LEN);
+ bx_options.optrom[0].Opath->set_format ("Name of optional ROM image #1 : %s");
+ bx_options.optrom[0].Oaddress = new bx_param_num_c (BXP_OPTROM1_ADDRESS,
+ "optional romaddr #1",
+ "The address at which the optional ROM image #1 should be loaded",
+ 0, BX_MAX_BIT32U,
+ 0);
+ bx_options.optrom[0].Oaddress->set_base (16);
+#if BX_WITH_WX
+ bx_options.optrom[0].Opath->set_label ("Optional ROM image #1");
+ bx_options.optrom[0].Oaddress->set_label ("Address");
+#else
+ bx_options.optrom[0].Oaddress->set_format ("optional ROM #1 address: 0x%05x");
+#endif
+
+ bx_options.optrom[1].Opath = new bx_param_filename_c (BXP_OPTROM2_PATH,
+ "optional romimage #2",
+ "Pathname of optional ROM image #2 to load",
+ "", BX_PATHNAME_LEN);
+ bx_options.optrom[1].Opath->set_format ("Name of optional ROM image #2 : %s");
+ bx_options.optrom[1].Oaddress = new bx_param_num_c (BXP_OPTROM2_ADDRESS,
+ "optional romaddr #2",
+ "The address at which the optional ROM image #2 should be loaded",
+ 0, BX_MAX_BIT32U,
+ 0);
+ bx_options.optrom[1].Oaddress->set_base (16);
+#if BX_WITH_WX
+ bx_options.optrom[1].Opath->set_label ("Optional ROM image #2");
+ bx_options.optrom[1].Oaddress->set_label ("Address");
+#else
+ bx_options.optrom[1].Oaddress->set_format ("optional ROM #2 address: 0x%05x");
+#endif
+
+ bx_options.optrom[2].Opath = new bx_param_filename_c (BXP_OPTROM3_PATH,
+ "optional romimage #3",
+ "Pathname of optional ROM image #3 to load",
+ "", BX_PATHNAME_LEN);
+ bx_options.optrom[2].Opath->set_format ("Name of optional ROM image #3 : %s");
+ bx_options.optrom[2].Oaddress = new bx_param_num_c (BXP_OPTROM3_ADDRESS,
+ "optional romaddr #3",
+ "The address at which the optional ROM image #3 should be loaded",
+ 0, BX_MAX_BIT32U,
+ 0);
+ bx_options.optrom[2].Oaddress->set_base (16);
+#if BX_WITH_WX
+ bx_options.optrom[2].Opath->set_label ("Optional ROM image #3");
+ bx_options.optrom[2].Oaddress->set_label ("Address");
+#else
+ bx_options.optrom[2].Oaddress->set_format ("optional ROM #3 address: 0x%05x");
+#endif
+
+ bx_options.optrom[3].Opath = new bx_param_filename_c (BXP_OPTROM4_PATH,
+ "optional romimage #4",
+ "Pathname of optional ROM image #4 to load",
+ "", BX_PATHNAME_LEN);
+ bx_options.optrom[3].Opath->set_format ("Name of optional ROM image #4 : %s");
+ bx_options.optrom[3].Oaddress = new bx_param_num_c (BXP_OPTROM4_ADDRESS,
+ "optional romaddr #4",
+ "The address at which the optional ROM image #4 should be loaded",
+ 0, BX_MAX_BIT32U,
+ 0);
+ bx_options.optrom[3].Oaddress->set_base (16);
+#if BX_WITH_WX
+ bx_options.optrom[3].Opath->set_label ("Optional ROM image #4");
+ bx_options.optrom[3].Oaddress->set_label ("Address");
+#else
+ bx_options.optrom[3].Oaddress->set_format ("optional ROM #4 address: 0x%05x");
+#endif
+
+ bx_options.vgarom.Opath = new bx_param_filename_c (BXP_VGA_ROM_PATH,
+ "vgaromimage",
+ "Pathname of VGA ROM image to load",
+ "", BX_PATHNAME_LEN);
+ bx_options.vgarom.Opath->set_format ("Name of VGA BIOS image: %s");
+#if BX_WITH_WX
+ bx_options.vgarom.Opath->set_label ("VGA BIOS image");
+#endif
+ bx_param_c *memory_init_list[] = {
+ bx_options.memory.Osize,
+ bx_options.vgarom.Opath,
+ bx_options.rom.Opath,
+ bx_options.rom.Oaddress,
+ bx_options.optrom[0].Opath,
+ bx_options.optrom[0].Oaddress,
+ bx_options.optrom[1].Opath,
+ bx_options.optrom[1].Oaddress,
+ bx_options.optrom[2].Opath,
+ bx_options.optrom[2].Oaddress,
+ bx_options.optrom[3].Opath,
+ bx_options.optrom[3].Oaddress,
+ NULL
+ };
+ menu = new bx_list_c (BXP_MENU_MEMORY, "Bochs Memory Options", "memmenu", memory_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+
+ // interface
+ bx_options.Ovga_update_interval = new bx_param_num_c (BXP_VGA_UPDATE_INTERVAL,
+ "VGA Update Interval",
+ "Number of microseconds between VGA updates",
+ 1, BX_MAX_BIT32U,
+ 30000);
+ bx_options.Ovga_update_interval->set_handler (bx_param_handler);
+ bx_options.Ovga_update_interval->set_runtime_param (1);
+ bx_options.Ovga_update_interval->set_ask_format ("Type a new value for VGA update interval: [%d] ");
+ bx_options.Omouse_enabled = new bx_param_bool_c (BXP_MOUSE_ENABLED,
+ "Enable the mouse",
+ "Controls whether the mouse sends events to the guest. The hardware emulation is always enabled.",
+ 0);
+ bx_options.Omouse_enabled->set_handler (bx_param_handler);
+ bx_options.Omouse_enabled->set_runtime_param (1);
+ bx_options.Oips = new bx_param_num_c (BXP_IPS,
+ "Emulated instructions per second (IPS)",
+ "Emulated instructions per second, used to calibrate bochs emulated time with wall clock time.",
+ 1, BX_MAX_BIT32U,
+ 500000);
+ bx_options.Otext_snapshot_check = new bx_param_bool_c (BXP_TEXT_SNAPSHOT_CHECK,
+ "Enable panic for use in bochs testing",
+ "Enable panic when text on screen matches snapchk.txt.\nUseful for regression testing.\nIn win32, turns off CR/LF in snapshots and cuts.",
+ 0);
+ bx_options.Oprivate_colormap = new bx_param_bool_c (BXP_PRIVATE_COLORMAP,
+ "Use a private colormap",
+ "Request that the GUI create and use it's own non-shared colormap. This colormap will be used when in the bochs window. If not enabled, a shared colormap scheme may be used. Not implemented on all GUI's.",
+ 0);
+#if BX_WITH_AMIGAOS
+ bx_options.Ofullscreen = new bx_param_bool_c (BXP_FULLSCREEN,
+ "Use full screen mode",
+ "When enabled, bochs occupies the whole screen instead of just a window.",
+ 0);
+ bx_options.Oscreenmode = new bx_param_string_c (BXP_SCREENMODE,
+ "Screen mode name",
+ "Screen mode name",
+ "", BX_PATHNAME_LEN);
+ bx_options.Oscreenmode->set_handler (bx_param_string_handler);
+#endif
+ static char *config_interface_list[] = {
+ "textconfig",
+#if BX_WITH_WX
+ "wx",
+#endif
+ NULL
+ };
+ bx_options.Osel_config = new bx_param_enum_c (
+ BXP_SEL_CONFIG_INTERFACE,
+ "Configuration interface",
+ "Select configuration interface",
+ config_interface_list,
+ 0,
+ 0);
+ bx_options.Osel_config->set_by_name (BX_DEFAULT_CONFIG_INTERFACE);
+ bx_options.Osel_config->set_ask_format ("Choose which configuration interface to use: [%s] ");
+ // this is a list of gui libraries that are known to be available at
+ // compile time. The one that is listed first will be the default,
+ // which is used unless the user overrides it on the command line or
+ // in a configuration file.
+ static char *display_library_list[] = {
+#if BX_WITH_X11
+ "x",
+#endif
+#if BX_WITH_WIN32
+ "win32",
+#endif
+#if BX_WITH_CARBON
+ "carbon",
+#endif
+#if BX_WITH_BEOS
+ "beos",
+#endif
+#if BX_WITH_MACOS
+ "macos",
+#endif
+#if BX_WITH_AMIGAOS
+ "amigaos",
+#endif
+#if BX_WITH_SDL
+ "sdl",
+#endif
+#if BX_WITH_SVGA
+ "svga",
+#endif
+#if BX_WITH_TERM
+ "term",
+#endif
+#if BX_WITH_RFB
+ "rfb",
+#endif
+#if BX_WITH_WX
+ "wx",
+#endif
+#if BX_WITH_NOGUI
+ "nogui",
+#endif
+ NULL
+ };
+ bx_options.Osel_displaylib = new bx_param_enum_c (BXP_SEL_DISPLAY_LIBRARY,
+ "VGA Display Library",
+ "Select VGA Display Library",
+ display_library_list,
+ 0,
+ 0);
+ bx_options.Osel_displaylib->set_by_name (BX_DEFAULT_DISPLAY_LIBRARY);
+ bx_options.Osel_displaylib->set_ask_format ("Choose which library to use for the Bochs display: [%s] ");
+ bx_param_c *interface_init_list[] = {
+ bx_options.Osel_config,
+ bx_options.Osel_displaylib,
+ bx_options.Ovga_update_interval,
+ bx_options.Omouse_enabled,
+ bx_options.Oips,
+ bx_options.Oprivate_colormap,
+#if BX_WITH_AMIGAOS
+ bx_options.Ofullscreen,
+ bx_options.Oscreenmode,
+#endif
+ NULL
+ };
+ menu = new bx_list_c (BXP_MENU_INTERFACE, "Bochs Interface Menu", "intfmenu", interface_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+
+ // NE2K options
+ bx_options.ne2k.Opresent = new bx_param_bool_c (BXP_NE2K_PRESENT,
+ "Enable NE2K NIC emulation",
+ "Enables the NE2K NIC emulation",
+ 0);
+ bx_options.ne2k.Oioaddr = new bx_param_num_c (BXP_NE2K_IOADDR,
+ "NE2K I/O Address",
+ "I/O base address of the emulated NE2K device",
+ 0, 0xffff,
+ 0x240);
+ bx_options.ne2k.Oioaddr->set_base (16);
+ bx_options.ne2k.Oirq = new bx_param_num_c (BXP_NE2K_IRQ,
+ "NE2K Interrupt",
+ "IRQ used by the NE2K device",
+ 0, 15,
+ 9);
+ bx_options.ne2k.Oirq->set_options (bx_param_num_c::USE_SPIN_CONTROL);
+ bx_options.ne2k.Omacaddr = new bx_param_string_c (BXP_NE2K_MACADDR,
+ "MAC Address",
+ "MAC address of the NE2K device. Don't use an address of a machine on your net.",
+ "\xfe\xfd\xde\xad\xbe\xef", 6);
+ bx_options.ne2k.Omacaddr->get_options ()->set (bx_options.ne2k.Omacaddr->RAW_BYTES);
+ bx_options.ne2k.Omacaddr->set_separator (':');
+ static char *eth_module_list[] = {
+ "null",
+#if defined(ETH_LINUX)
+ "linux",
+#endif
+#if HAVE_ETHERTAP
+ "tap",
+#endif
+#if HAVE_TUNTAP
+ "tuntap",
+#endif
+#if defined(ETH_WIN32)
+ "win32",
+#endif
+#if defined(ETH_FBSD)
+ "fbsd",
+#endif
+#ifdef ETH_ARPBACK
+ "arpback",
+#endif
+ NULL
+ };
+ bx_options.ne2k.Oethmod = new bx_param_enum_c (BXP_NE2K_ETHMOD,
+ "Ethernet module",
+ "Module used for the connection to the real net.",
+ eth_module_list,
+ 0,
+ 0);
+ bx_options.ne2k.Oethmod->set_by_name ("null");
+ bx_options.ne2k.Oethdev = new bx_param_string_c (BXP_NE2K_ETHDEV,
+ "Ethernet device",
+ "Device used for the connection to the real net. This is only valid if an ethernet module other than 'null' is used.",
+ "xl0", BX_PATHNAME_LEN);
+ bx_options.ne2k.Oscript = new bx_param_string_c (BXP_NE2K_SCRIPT,
+ "Device configuration script",
+ "Name of the script that is executed after Bochs initializes the network interface (optional).",
+ "none", BX_PATHNAME_LEN);
+#if !BX_WITH_WX
+ bx_options.ne2k.Oscript->set_ask_format ("Enter new script name, or 'none': [%s] ");
+#endif
+ bx_param_c *ne2k_init_list[] = {
+ bx_options.ne2k.Opresent,
+ bx_options.ne2k.Oioaddr,
+ bx_options.ne2k.Oirq,
+ bx_options.ne2k.Omacaddr,
+ bx_options.ne2k.Oethmod,
+ bx_options.ne2k.Oethdev,
+ bx_options.ne2k.Oscript,
+ NULL
+ };
+ menu = new bx_list_c (BXP_NE2K, "NE2K Configuration", "", ne2k_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+ bx_param_c **ne2k_dependent_list = &ne2k_init_list[1];
+ bx_options.ne2k.Opresent->set_dependent_list (
+ new bx_list_c (BXP_NULL, "", "", ne2k_dependent_list));
+ bx_options.ne2k.Opresent->set_handler (bx_param_handler);
+ bx_options.ne2k.Opresent->set (0);
+
+ // SB16 options
+ bx_options.sb16.Opresent = new bx_param_bool_c (BXP_SB16_PRESENT,
+ "Enable SB16 emulation",
+ "Enables the SB16 emulation",
+ 0);
+ bx_options.sb16.Omidifile = new bx_param_filename_c (BXP_SB16_MIDIFILE,
+ "MIDI file",
+ "The filename is where the MIDI data is sent. This can be device or just a file.",
+ "", BX_PATHNAME_LEN);
+ bx_options.sb16.Owavefile = new bx_param_filename_c (BXP_SB16_WAVEFILE,
+ "Wave file",
+ "This is the device/file where the wave output is stored",
+ "", BX_PATHNAME_LEN);
+ bx_options.sb16.Ologfile = new bx_param_filename_c (BXP_SB16_LOGFILE,
+ "Log file",
+ "The file to write the SB16 emulator messages to.",
+ "", BX_PATHNAME_LEN);
+ bx_options.sb16.Omidimode = new bx_param_num_c (BXP_SB16_MIDIMODE,
+ "Midi mode",
+ "Controls the MIDI output format.",
+ 0, 3,
+ 0);
+ bx_options.sb16.Owavemode = new bx_param_num_c (BXP_SB16_WAVEMODE,
+ "Wave mode",
+ "Controls the wave output format.",
+ 0, 3,
+ 0);
+ bx_options.sb16.Ologlevel = new bx_param_num_c (BXP_SB16_LOGLEVEL,
+ "Log mode",
+ "Controls how verbose the SB16 emulation is (0 = no log, 5 = all errors and infos).",
+ 0, 5,
+ 0);
+ bx_options.sb16.Odmatimer = new bx_param_num_c (BXP_SB16_DMATIMER,
+ "DMA timer",
+ "Microseconds per second for a DMA cycle.",
+ 0, BX_MAX_BIT32U,
+ 0);
+
+#if BX_WITH_WX
+ bx_options.sb16.Omidimode->set_options (bx_param_num_c::USE_SPIN_CONTROL);
+ bx_options.sb16.Owavemode->set_options (bx_param_num_c::USE_SPIN_CONTROL);
+ bx_options.sb16.Ologlevel->set_options (bx_param_num_c::USE_SPIN_CONTROL);
+#endif
+ bx_param_c *sb16_init_list[] = {
+ bx_options.sb16.Opresent,
+ bx_options.sb16.Omidimode,
+ bx_options.sb16.Omidifile,
+ bx_options.sb16.Owavemode,
+ bx_options.sb16.Owavefile,
+ bx_options.sb16.Ologlevel,
+ bx_options.sb16.Ologfile,
+ bx_options.sb16.Odmatimer,
+ NULL
+ };
+ menu = new bx_list_c (BXP_SB16, "SB16 Configuration", "", sb16_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+ // sb16_dependent_list is a null-terminated list including all the
+ // sb16 fields except for the "present" field. These will all be enabled/
+ // disabled according to the value of the present field.
+ bx_param_c **sb16_dependent_list = &sb16_init_list[1];
+ bx_options.sb16.Opresent->set_dependent_list (
+ new bx_list_c (BXP_NULL, "", "", sb16_dependent_list));
+
+ bx_options.log.Ofilename = new bx_param_filename_c (BXP_LOG_FILENAME,
+ "Log filename",
+ "Pathname of bochs log file",
+ "-", BX_PATHNAME_LEN);
+ bx_options.log.Ofilename->set_ask_format ("Enter log filename: [%s] ");
+
+ bx_options.log.Oprefix = new bx_param_string_c (BXP_LOG_PREFIX,
+ "Log output prefix",
+ "Prefix prepended to log output",
+ "%t%e%d", BX_PATHNAME_LEN);
+ bx_options.log.Oprefix->set_ask_format ("Enter log prefix: [%s] ");
+
+ bx_options.log.Odebugger_filename = new bx_param_filename_c (BXP_DEBUGGER_LOG_FILENAME,
+ "Debugger Log filename",
+ "Pathname of debugger log file",
+ "-", BX_PATHNAME_LEN);
+ bx_options.log.Odebugger_filename->set_ask_format ("Enter debugger log filename: [%s] ");
+
+ // loader
+ bx_options.load32bitOSImage.OwhichOS = new bx_param_enum_c (BXP_LOAD32BITOS_WHICH,
+ "Which operating system?",
+ "Which OS to boot",
+ loader_os_names,
+#ifdef BX_USE_VMX
+ Load32bitOSLinux,
+#else
+ Load32bitOSNone,
+#endif
+ Load32bitOSNone);
+ bx_options.load32bitOSImage.Opath = new bx_param_filename_c (BXP_LOAD32BITOS_PATH,
+ "Pathname of OS to load",
+ "Pathname of the 32-bit OS to load",
+ "", BX_PATHNAME_LEN);
+ bx_options.load32bitOSImage.Oiolog = new bx_param_filename_c (BXP_LOAD32BITOS_IOLOG,
+ "Pathname of I/O log file",
+ "I/O logfile used for initializing the hardware",
+ "", BX_PATHNAME_LEN);
+ bx_options.load32bitOSImage.Oinitrd = new bx_param_filename_c (BXP_LOAD32BITOS_INITRD,
+ "Pathname of initrd",
+ "Pathname of the initial ramdisk",
+ "", BX_PATHNAME_LEN);
+ bx_param_c *loader_init_list[] = {
+ bx_options.load32bitOSImage.OwhichOS,
+ bx_options.load32bitOSImage.Opath,
+ bx_options.load32bitOSImage.Oiolog,
+ bx_options.load32bitOSImage.Oinitrd,
+ NULL
+ };
+ bx_options.load32bitOSImage.OwhichOS->set_format ("os=%s");
+ bx_options.load32bitOSImage.Opath->set_format ("path=%s");
+ bx_options.load32bitOSImage.Oiolog->set_format ("iolog=%s");
+ bx_options.load32bitOSImage.Oinitrd->set_format ("initrd=%s");
+ bx_options.load32bitOSImage.OwhichOS->set_ask_format ("Enter OS to load: [%s] ");
+ bx_options.load32bitOSImage.Opath->set_ask_format ("Enter pathname of OS: [%s]");
+ bx_options.load32bitOSImage.Oiolog->set_ask_format ("Enter pathname of I/O log: [%s] ");
+ bx_options.load32bitOSImage.Oinitrd->set_ask_format ("Enter pathname of initrd: [%s] ");
+ menu = new bx_list_c (BXP_LOAD32BITOS, "32-bit OS Loader", "", loader_init_list);
+ menu->get_options ()->set (menu->SERIES_ASK);
+ bx_options.load32bitOSImage.OwhichOS->set_handler (bx_param_handler);
+#ifdef BX_USE_VMX
+ bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSLinux);
+#else
+ bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSNone);
+#endif
+
+ // clock
+ bx_options.clock.Otime0 = new bx_param_num_c (BXP_CLOCK_TIME0,
+ "clock:time0",
+ "Initial time for Bochs CMOS clock, used if you really want two runs to be identical",
+ 0, BX_MAX_BIT32U,
+ BX_CLOCK_TIME0_LOCAL);
+ bx_options.clock.Osync = new bx_param_enum_c (BXP_CLOCK_SYNC,
+ "clock:sync",
+ "Host to guest time synchronization method",
+ clock_sync_names,
+ BX_CLOCK_SYNC_NONE,
+ BX_CLOCK_SYNC_NONE);
+ bx_param_c *clock_init_list[] = {
+ bx_options.clock.Osync,
+ bx_options.clock.Otime0,
+ NULL
+ };
+#if !BX_WITH_WX
+ bx_options.clock.Osync->set_format ("sync=%s");
+ bx_options.clock.Otime0->set_format ("initial time=%d");
+#endif
+ bx_options.clock.Otime0->set_ask_format ("Enter Initial CMOS time (1:localtime, 2:utc, other:time in seconds): [%d] ");
+ bx_options.clock.Osync->set_ask_format ("Enter Synchronisation method: [%s] ");
+ bx_options.clock.Otime0->set_label ("Initial CMOS time for Bochs\n(1:localtime, 2:utc, other:time in seconds)");
+ bx_options.clock.Osync->set_label ("Synchronisation method");
+ menu = new bx_list_c (BXP_CLOCK, "Clock parameters", "", clock_init_list);
+ menu->get_options ()->set (menu->SERIES_ASK);
+
+ // other
+ bx_options.Okeyboard_serial_delay = new bx_param_num_c (BXP_KBD_SERIAL_DELAY,
+ "Keyboard serial delay",
+ "Approximate time in microseconds that it takes one character to be transfered from the keyboard to controller over the serial path.",
+ 1, BX_MAX_BIT32U,
+ 20000);
+ bx_options.Okeyboard_paste_delay = new bx_param_num_c (BXP_KBD_PASTE_DELAY,
+ "Keyboard paste delay",
+ "Approximate time in microseconds between attemps to paste characters to the keyboard controller.",
+ 1000, BX_MAX_BIT32U,
+ 100000);
+ bx_options.Okeyboard_paste_delay->set_handler (bx_param_handler);
+ bx_options.Okeyboard_paste_delay->set_runtime_param (1);
+ bx_options.Ofloppy_command_delay = new bx_param_num_c (BXP_FLOPPY_CMD_DELAY,
+ "Floppy command delay",
+ "Time in microseconds to wait before completing some floppy commands such as read/write/seek/etc, which normally have a delay associated. This used to be hardwired to 50,000 before.",
+ 1, BX_MAX_BIT32U,
+ 50000);
+ bx_options.Oi440FXSupport = new bx_param_bool_c (BXP_I440FX_SUPPORT,
+ "PCI i440FX Support",
+ "Controls whether to emulate the i440FX PCI chipset",
+ 0);
+ bx_options.cmos.OcmosImage = new bx_param_bool_c (BXP_CMOS_IMAGE,
+ "Use a CMOS image",
+ "Controls the usage of a CMOS image",
+ 0);
+ bx_options.cmos.Opath = new bx_param_filename_c (BXP_CMOS_PATH,
+ "Pathname of CMOS image",
+ "Pathname of CMOS image",
+ "", BX_PATHNAME_LEN);
+ deplist = new bx_list_c (BXP_NULL, 1);
+ deplist->add (bx_options.cmos.Opath);
+ bx_options.cmos.OcmosImage->set_dependent_list (deplist);
+
+ // Keyboard mapping
+ bx_options.keyboard.OuseMapping = new bx_param_bool_c(BXP_KEYBOARD_USEMAPPING,
+ "Use keyboard mapping",
+ "Controls whether to use the keyboard mapping feature",
+ 0);
+ bx_options.keyboard.Okeymap = new bx_param_filename_c (BXP_KEYBOARD_MAP,
+ "Keymap filename",
+ "Pathname of the keymap file used",
+ "", BX_PATHNAME_LEN);
+ deplist = new bx_list_c (BXP_NULL, 1);
+ deplist->add (bx_options.keyboard.Okeymap);
+ bx_options.keyboard.OuseMapping->set_dependent_list (deplist);
+
+ // Keyboard type
+ bx_options.Okeyboard_type = new bx_param_enum_c (BXP_KBD_TYPE,
+ "Keyboard type",
+ "Keyboard type reported by the 'identify keyboard' command",
+ keyboard_type_names,
+ BX_KBD_MF_TYPE,
+ BX_KBD_XT_TYPE);
+ bx_options.Okeyboard_type->set_ask_format ("Enter keyboard type: [%s] ");
+
+ // Userbutton shortcut
+ bx_options.Ouser_shortcut = new bx_param_string_c (BXP_USER_SHORTCUT,
+ "Userbutton shortcut",
+ "Defines the keyboard shortcut to be sent when you press the 'user' button in the headerbar.",
+ "none", 16);
+
+ // GDB stub
+ bx_options.gdbstub.port = 1234;
+ bx_options.gdbstub.text_base = 0;
+ bx_options.gdbstub.data_base = 0;
+ bx_options.gdbstub.bss_base = 0;
+
+ bx_param_c *keyboard_init_list[] = {
+ bx_options.Okeyboard_serial_delay,
+ bx_options.Okeyboard_paste_delay,
+ bx_options.keyboard.OuseMapping,
+ bx_options.keyboard.Okeymap,
+ bx_options.Okeyboard_type,
+ bx_options.Ouser_shortcut,
+ NULL
+ };
+ menu = new bx_list_c (BXP_MENU_KEYBOARD, "Configure Keyboard", "", keyboard_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+
+ bx_param_c *other_init_list[] = {
+ bx_options.Ofloppy_command_delay,
+ bx_options.Oi440FXSupport,
+ bx_options.cmos.OcmosImage,
+ bx_options.cmos.Opath,
+ SIM->get_param (BXP_CLOCK),
+ SIM->get_param (BXP_LOAD32BITOS),
+ NULL
+ };
+ menu = new bx_list_c (BXP_MENU_MISC, "Configure Everything Else", "", other_init_list);
+ menu->get_options ()->set (menu->SHOW_PARENT);
+
+#if BX_WITH_WX
+ bx_param_c *other_init_list2[] = {
+// bx_options.Osel_config,
+// bx_options.Osel_displaylib,
+ bx_options.Ovga_update_interval,
+ bx_options.log.Oprefix,
+ bx_options.Omouse_enabled,
+ bx_options.OfloppySigCheck,
+ bx_options.Ofloppy_command_delay,
+ bx_options.OnewHardDriveSupport,
+ bx_options.Oprivate_colormap,
+#if BX_WITH_AMIGAOS
+ bx_options.Ofullscreen,
+ bx_options.Oscreenmode,
+#endif
+ bx_options.Oi440FXSupport,
+ bx_options.cmos.OcmosImage,
+ bx_options.cmos.Opath,
+ NULL
+ };
+ menu = new bx_list_c (BXP_MENU_MISC_2, "Other options", "", other_init_list2);
+#endif
+}
+
+void bx_reset_options ()
+{
+ // drives
+ bx_options.floppya.Opath->reset();
+ bx_options.floppya.Odevtype->reset();
+ bx_options.floppya.Otype->reset();
+ bx_options.floppya.Ostatus->reset();
+ bx_options.floppyb.Opath->reset();
+ bx_options.floppyb.Odevtype->reset();
+ bx_options.floppyb.Otype->reset();
+ bx_options.floppyb.Ostatus->reset();
+
+ for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ bx_options.ata[channel].Opresent->reset();
+ bx_options.ata[channel].Oioaddr1->reset();
+ bx_options.ata[channel].Oioaddr2->reset();
+ bx_options.ata[channel].Oirq->reset();
+
+ for (Bit8u slave=0; slave<2; slave++) {
+ bx_options.atadevice[channel][slave].Opresent->reset();
+ bx_options.atadevice[channel][slave].Otype->reset();
+ bx_options.atadevice[channel][slave].Omode->reset();
+ bx_options.atadevice[channel][slave].Opath->reset();
+ bx_options.atadevice[channel][slave].Ocylinders->reset();
+ bx_options.atadevice[channel][slave].Oheads->reset();
+ bx_options.atadevice[channel][slave].Ospt->reset();
+ bx_options.atadevice[channel][slave].Ostatus->reset();
+ bx_options.atadevice[channel][slave].Omodel->reset();
+ bx_options.atadevice[channel][slave].Obiosdetect->reset();
+ bx_options.atadevice[channel][slave].Otranslation->reset();
+ }
+ }
+ bx_options.OnewHardDriveSupport->reset();
+
+ // boot & memory
+ bx_options.Obootdrive->reset();
+ bx_options.OfloppySigCheck->reset();
+ bx_options.memory.Osize->reset();
+
+ // standard ports
+ bx_options.com[0].Oenabled->reset();
+ bx_options.com[0].Odev->reset();
+ bx_options.par[0].Oenabled->reset();
+ bx_options.par[0].Ooutfile->reset();
+
+ // rom images
+ bx_options.rom.Opath->reset();
+ bx_options.rom.Oaddress->reset();
+ bx_options.optrom[0].Opath->reset();
+ bx_options.optrom[0].Oaddress->reset();
+ bx_options.optrom[1].Opath->reset();
+ bx_options.optrom[1].Oaddress->reset();
+ bx_options.optrom[2].Opath->reset();
+ bx_options.optrom[2].Oaddress->reset();
+ bx_options.optrom[3].Opath->reset();
+ bx_options.optrom[3].Oaddress->reset();
+ bx_options.vgarom.Opath->reset();
+
+ // interface
+ bx_options.Ovga_update_interval->reset();
+ bx_options.Omouse_enabled->reset();
+ bx_options.Oips->reset();
+ bx_options.Oprivate_colormap->reset();
+#if BX_WITH_AMIGAOS
+ bx_options.Ofullscreen->reset();
+ bx_options.Oscreenmode->reset();
+#endif
+
+ // ne2k
+ bx_options.ne2k.Opresent->reset();
+ bx_options.ne2k.Oioaddr->reset();
+ bx_options.ne2k.Oirq->reset();
+ bx_options.ne2k.Omacaddr->reset();
+ bx_options.ne2k.Oethmod->reset();
+ bx_options.ne2k.Oethdev->reset();
+ bx_options.ne2k.Oscript->reset();
+
+ // SB16
+ bx_options.sb16.Opresent->reset();
+ bx_options.sb16.Omidifile->reset();
+ bx_options.sb16.Owavefile->reset();
+ bx_options.sb16.Ologfile->reset();
+ bx_options.sb16.Omidimode->reset();
+ bx_options.sb16.Owavemode->reset();
+ bx_options.sb16.Ologlevel->reset();
+ bx_options.sb16.Odmatimer->reset();
+
+ // logfile
+ bx_options.log.Ofilename->reset();
+ bx_options.log.Oprefix->reset();
+ bx_options.log.Odebugger_filename->reset();
+
+ // loader
+ bx_options.load32bitOSImage.OwhichOS->reset();
+ bx_options.load32bitOSImage.Opath->reset();
+ bx_options.load32bitOSImage.Oiolog->reset();
+ bx_options.load32bitOSImage.Oinitrd->reset();
+
+ // keyboard
+ bx_options.Okeyboard_serial_delay->reset();
+ bx_options.Okeyboard_paste_delay->reset();
+ bx_options.keyboard.OuseMapping->reset();
+ bx_options.keyboard.Okeymap->reset();
+ bx_options.Okeyboard_type->reset();
+ bx_options.Ouser_shortcut->reset();
+
+ // Clock
+ bx_options.clock.Otime0->reset();
+ bx_options.clock.Osync->reset();
+
+ // other
+ bx_options.Ofloppy_command_delay->reset();
+ bx_options.Oi440FXSupport->reset();
+ bx_options.cmos.OcmosImage->reset();
+ bx_options.cmos.Opath->reset();
+ bx_options.Otext_snapshot_check->reset();
+}
+
+void bx_print_header ()
+{
+ fprintf (stderr, "%s\n", divider);
+ char buffer[128];
+ sprintf (buffer, "Bochs x86 Emulator %s\n", VER_STRING);
+ bx_center_print (stderr, buffer, 72);
+ if (REL_STRING[0]) {
+ sprintf (buffer, "%s\n", REL_STRING);
+ bx_center_print (stderr, buffer, 72);
+ }
+ fprintf (stderr, "%s\n", divider);
+}
+
+#if BX_WITH_CARBON
+/* Original code by Darrell Walisser - dwaliss1@purdue.edu */
+
+static void setupWorkingDirectory (char *path)
+{
+ char parentdir[MAXPATHLEN];
+ char *c;
+
+ strncpy ( parentdir, path, MAXPATHLEN );
+ c = (char*) parentdir;
+
+ while (*c != '\0') /* go to end */
+ c++;
+
+ while (*c != '/') /* back up to parent */
+ c--;
+
+ *c = '\0'; /* cut off last part (binary name) */
+
+ /* chdir to the binary app's parent */
+ int n;
+ n = chdir (parentdir);
+ if (n) BX_PANIC (("failed to change dir to parent"));
+ /* chdir to the .app's parent */
+ n = chdir ("../../../");
+ if (n) BX_PANIC (("failed to change to ../../.."));
+}
+
+/* Panic button to display fatal errors.
+ Completely self contained, can't rely on carbon.cc being available */
+static void carbonFatalDialog(const char *error, const char *exposition)
+{
+ DialogRef alertDialog;
+ CFStringRef cfError;
+ CFStringRef cfExposition;
+ DialogItemIndex index;
+ AlertStdCFStringAlertParamRec alertParam = {0};
+ fprintf(stderr, "Entering carbonFatalDialog: %s\n", error);
+
+ // Init libraries
+ InitCursor();
+ // Assemble dialog
+ cfError = CFStringCreateWithCString(NULL, error, kCFStringEncodingASCII);
+ if(exposition != NULL)
+ {
+ cfExposition = CFStringCreateWithCString(NULL, exposition, kCFStringEncodingASCII);
+ }
+ else { cfExposition = NULL; }
+ alertParam.version = kStdCFStringAlertVersionOne;
+ alertParam.defaultText = CFSTR("Quit");
+ alertParam.position = kWindowDefaultPosition;
+ alertParam.defaultButton = kAlertStdAlertOKButton;
+ // Display Dialog
+ CreateStandardAlert(
+ kAlertStopAlert,
+ cfError,
+ cfExposition, /* can be NULL */
+ &alertParam, /* can be NULL */
+ &alertDialog);
+ RunStandardAlert( alertDialog, NULL, &index);
+ // Cleanup
+ CFRelease( cfError );
+ if( cfExposition != NULL ) { CFRelease( cfExposition ); }
+}
+#endif
+
+int bxmain () {
+#ifdef HAVE_LOCALE_H
+ // Initialize locale (for isprint() and other functions)
+ setlocale (LC_ALL, "");
+#endif
+ bx_user_quit = 0;
+ bx_init_siminterface (); // create the SIM object
+
+ static jmp_buf context;
+ if (setjmp (context) == 0) {
+ SIM->set_quit_context (&context);
+ if (bx_init_main (bx_startup_flags.argc, bx_startup_flags.argv) < 0)
+ return 0;
+ // read a param to decide which config interface to start.
+ // If one exists, start it. If not, just begin.
+ bx_param_enum_c *ci_param = SIM->get_param_enum (BXP_SEL_CONFIG_INTERFACE);
+ char *ci_name = ci_param->get_choice (ci_param->get ());
+ if (!strcmp(ci_name, "textconfig")) {
+ init_text_config_interface (); // in textconfig.h
+ }
+#if BX_WITH_WX
+ else if (!strcmp(ci_name, "wx")) {
+ PLUG_load_plugin(wx, PLUGTYPE_CORE);
+ }
+#endif
+ else {
+ BX_PANIC (("unsupported configuration interface '%s'", ci_name));
+ }
+ int status = SIM->configuration_interface (ci_name, CI_START);
+ if (status == CI_ERR_NO_TEXT_CONSOLE)
+ BX_PANIC (("Bochs needed the text console, but it was not usable"));
+ // user quit the config interface, so just quit
+ } else {
+ // quit via longjmp
+ }
+}
+
+// normal main function, presently in for all cases except for
+// wxWindows under win32.
+int main (int argc, char *argv[])
+{
+ daemon(0, 0);
+ bx_startup_flags.argc = argc;
+ bx_startup_flags.argv = argv;
+#if BX_WITH_SDL && defined(WIN32)
+ // if SDL/win32, try to create a console window.
+ RedirectIOToConsole ();
+#endif
+ return bxmain ();
+}
+
+void
+print_usage ()
+{
+ fprintf(stderr,
+ "Usage: bochs [flags] [bochsrc options]\n\n"
+ " -n no configuration file\n"
+ " -f configfile specify configuration file\n"
+ " -q quick start (skip configuration interface)\n"
+ " --help display this help and exit\n\n"
+ "For information on Bochs configuration file arguments, see the\n"
+#if (!defined(WIN32)) && !BX_WITH_MACOS
+ "bochsrc section in the user documentation or the man page of bochsrc.\n");
+#else
+ "bochsrc section in the user documentation.\n");
+#endif
+}
+
+#ifdef BX_USE_VMX
+int domid = -1;
+unsigned long megabytes = 0;
+#endif
+int
+bx_init_main (int argc, char *argv[])
+{
+ // To deal with initialization order problems inherent in C++, use the macros
+ // SAFE_GET_IOFUNC and SAFE_GET_GENLOG to retrieve "io" and "genlog" in all
+ // constructors or functions called by constructors. The macros test for
+ // NULL and create the object if necessary, then return it. Ensure that io
+ // and genlog get created, by making one reference to each macro right here.
+ // All other code can reference io and genlog directly. Because these
+ // objects are required for logging, and logging is so fundamental to
+ // knowing what the program is doing, they are never free()d.
+ SAFE_GET_IOFUNC(); // never freed
+ SAFE_GET_GENLOG(); // never freed
+
+ // initalization must be done early because some destructors expect
+ // the bx_options to exist by the time they are called.
+ bx_init_bx_dbg ();
+ bx_init_options ();
+
+ bx_print_header ();
+
+#ifdef BX_USE_VMX
+ xc_handle = xc_interface_open();
+ SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START);
+#else
+ SIM->get_param_enum(BXP_BOCHS_START)->set (BX_RUN_START);
+#endif
+
+ // interpret the args that start with -, like -q, -f, etc.
+ int arg = 1, load_rcfile=1;
+ while (arg < argc) {
+ // parse next arg
+ if (!strcmp ("--help", argv[arg]) || !strncmp ("-h", argv[arg], 2)) {
+ print_usage();
+ SIM->quit_sim (0);
+ }
+ else if (!strcmp ("-n", argv[arg])) {
+ load_rcfile = 0;
+ }
+ else if (!strcmp ("-q", argv[arg])) {
+ SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START);
+ }
+ else if (!strcmp ("-f", argv[arg])) {
+ if (++arg >= argc) BX_PANIC(("-f must be followed by a filename"));
+ else bochsrc_filename = argv[arg];
+ }
+ else if (!strcmp ("-qf", argv[arg])) {
+ SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START);
+ if (++arg >= argc) BX_PANIC(("-qf must be followed by a filename"));
+ else bochsrc_filename = argv[arg];
+ }
+#ifdef BX_USE_VMX
+ else if (!strcmp ("-p", argv[arg])) {
+ //get the polling port
+ extern int ioreq_port;
+ if (++arg >= argc) BX_PANIC(("-p must be followed by a polling port"));
+ else sscanf(argv[arg], "%d", &ioreq_port);
+ }
+ else if (!strcmp ("-d", argv[arg])) {
+ //get the domain id
+ if (++arg >= argc) BX_PANIC(("-d must be followed by domainid"));
+ else sscanf(argv[arg], "%d", &domid);
+ }
+ else if (!strcmp ("-m", argv[arg])) {
+ //get the maxmem
+ if (++arg >= argc)
+ BX_PANIC(("-m must be followed by maxmem in megabytes"));
+ else sscanf(argv[arg], "%d", &megabytes);
+ }
+
+#endif
+ else if (argv[arg][0] == '-') {
+ print_usage();
+ BX_PANIC (("command line arg '%s' was not understood", argv[arg]));
+ }
+ else {
+ // the arg did not start with -, so stop interpreting flags
+ break;
+ }
+ arg++;
+ }
+
+ int norcfile = 1;
+
+ if (load_rcfile) {
+ /* parse configuration file and command line arguments */
+#ifdef WIN32
+ if (bochsrc_filename != NULL) {
+ lstrcpy(bx_startup_flags.initial_dir, bochsrc_filename);
+ } else {
+ bx_startup_flags.initial_dir[0] = 0;
+ }
+#endif
+ if (bochsrc_filename == NULL) bochsrc_filename = bx_find_bochsrc ();
+ if (bochsrc_filename)
+ norcfile = bx_read_configuration (bochsrc_filename);
+ }
+
+ // parse the rest of the command line. This is done after reading the
+ // configuration file so that the command line arguments can override
+ // the settings from the file.
+ if (bx_parse_cmdline (arg, argc, argv)) {
+ BX_PANIC(("There were errors while parsing the command line"));
+ return -1;
+ }
+ // initialize plugin system. This must happen before we attempt to
+ // load any modules.
+ plugin_startup();
+ return 0;
+}
+
+bx_bool load_and_init_display_lib () {
+ if (bx_gui != NULL) {
+ // bx_gui has already been filled in. This happens when you start
+ // the simulation for the second time.
+ // Also, if you load wxWindows as the configuration interface. Its
+ // plugin_init will install wxWindows as the bx_gui.
+ return true;
+ }
+ BX_ASSERT (bx_gui == NULL);
+ bx_param_enum_c *ci_param = SIM->get_param_enum (BXP_SEL_CONFIG_INTERFACE);
+ char *ci_name = ci_param->get_choice (ci_param->get ());
+ bx_param_enum_c *gui_param = SIM->get_param_enum(BXP_SEL_DISPLAY_LIBRARY);
+ char *gui_name = gui_param->get_choice (gui_param->get ());
+ if (!strcmp(ci_name, "wx")) {
+ BX_ERROR(("change of the config interface to wx not implemented yet"));
+ }
+ if (!strcmp (gui_name, "wx")) {
+ // they must not have used wx as the configuration interface, or bx_gui
+ // would already be initialized. Sorry, it doesn't work that way.
+ BX_ERROR (("wxWindows was not used as the configuration interface, so it cannot be used as the display library"));
+ // choose another, hopefully different!
+ gui_param->set (0);
+ gui_name = gui_param->get_choice (gui_param->get ());
+ if (!strcmp (gui_name, "wx")) {
+ BX_PANIC (("no alternative display libraries are available"));
+ return false;
+ }
+ BX_ERROR (("changing display library to '%s' instead", gui_name));
+ }
+#if BX_WITH_NOGUI
+ if (!strcmp (gui_name, "nogui"))
+ PLUG_load_plugin (nogui, PLUGTYPE_OPTIONAL);
+#endif
+#if BX_WITH_RFB
+ if (!strcmp (gui_name, "rfb"))
+ PLUG_load_plugin (rfb, PLUGTYPE_OPTIONAL);
+#endif
+#if BX_WITH_X11
+ if (!strcmp (gui_name, "x"))
+ PLUG_load_plugin (x, PLUGTYPE_OPTIONAL);
+#endif
+
+#if BX_GUI_SIGHANDLER
+ // set the flag for guis requiring a GUI sighandler.
+ // useful when guis are compiled as plugins
+ // only term for now
+ if (!strcmp (gui_name, "term")) {
+ bx_gui_sighandler = 1;
+ }
+#endif
+
+ BX_ASSERT (bx_gui != NULL);
+ return true;
+}
+
+int
+bx_begin_simulation (int argc, char *argv[])
+{
+ // deal with gui selection
+ if (!load_and_init_display_lib ()) {
+ BX_PANIC (("no gui module was loaded"));
+ return 0;
+ }
+#if BX_GDBSTUB
+ // If using gdbstub, it will take control and call
+ // bx_init_hardware() and cpu_loop()
+ bx_gdbstub_init (argc, argv);
+#elif BX_DEBUGGER
+ // If using the debugger, it will take control and call
+ // bx_init_hardware() and cpu_loop()
+ bx_dbg_main(argc, argv);
+#else
+
+ bx_init_hardware();
+
+ if (bx_options.load32bitOSImage.OwhichOS->get ()) {
+ void bx_load32bitOSimagehack(void);
+ bx_load32bitOSimagehack();
+ }
+
+ SIM->set_init_done (1);
+
+ // update headerbar buttons since drive status can change during init
+ bx_gui->update_drive_status_buttons ();
+
+ // The set handler for mouse_enabled does not actually update the gui
+ // until init_done is set. This forces the set handler to be called,
+ // which sets up the mouse enabled GUI-specific stuff correctly.
+ // Not a great solution but it works. BBD
+ bx_options.Omouse_enabled->set (bx_options.Omouse_enabled->get ());
+
+ if (BX_SMP_PROCESSORS == 1) {
+ // only one processor, run as fast as possible by not messing with
+ // quantums and loops.
+ BX_CPU(0)->cpu_loop(1);
+ // for one processor, the only reason for cpu_loop to return is
+ // that kill_bochs_request was set by the GUI interface.
+ } else {
+ // SMP simulation: do a few instructions on each processor, then switch
+ // to another. Increasing quantum speeds up overall performance, but
+ // reduces granularity of synchronization between processors.
+ int processor = 0;
+ int quantum = 5;
+ while (1) {
+ // do some instructions in each processor
+ BX_CPU(processor)->cpu_loop(quantum);
+ processor = (processor+1) % BX_SMP_PROCESSORS;
+ if (BX_CPU(0)->kill_bochs_request)
+ break;
+ if (processor == 0)
+ BX_TICKN(quantum);
+ }
+ }
+#endif
+ BX_INFO (("cpu loop quit, shutting down simulator"));
+ bx_atexit ();
+ return(0);
+}
+
+
+int
+bx_read_configuration (char *rcfile)
+{
+ // parse rcfile first, then parse arguments in order.
+ BX_INFO (("reading configuration from %s", rcfile));
+ if (parse_bochsrc(rcfile) < 0) {
+ BX_PANIC (("reading from %s failed", rcfile));
+ return -1;
+ }
+ // update log actions
+ for (int level=0; level<N_LOGLEV; level++) {
+ int action = SIM->get_default_log_action (level);
+ io->set_log_action (level, action);
+ }
+ return 0;
+}
+
+int bx_parse_cmdline (int arg, int argc, char *argv[])
+{
+ //if (arg < argc) BX_INFO (("parsing command line arguments"));
+
+ while (arg < argc) {
+ BX_INFO (("parsing arg %d, %s", arg, argv[arg]));
+ parse_line_unformatted("cmdline args", argv[arg]);
+ arg++;
+ }
+ // update log actions
+ for (int level=0; level<N_LOGLEV; level++) {
+ int action = SIM->get_default_log_action (level);
+ io->set_log_action (level, action);
+ }
+ return 0;
+}
+
+ int
+bx_init_hardware()
+{
+ // all configuration has been read, now initialize everything.
+
+ if (SIM->get_param_enum(BXP_BOCHS_START)->get ()==BX_QUICK_START) {
+ for (int level=0; level<N_LOGLEV; level++) {
+ int action = SIM->get_default_log_action (level);
+#if !BX_USE_CONFIG_INTERFACE
+ if (action == ACT_ASK) action = ACT_FATAL;
+#endif
+ io->set_log_action (level, action);
+ }
+ }
+
+ bx_pc_system.init_ips(bx_options.Oips->get ());
+
+ if(bx_options.log.Ofilename->getptr()[0]!='-') {
+ BX_INFO (("using log file %s", bx_options.log.Ofilename->getptr ()));
+ io->init_log(bx_options.log.Ofilename->getptr ());
+ }
+
+ io->set_log_prefix(bx_options.log.Oprefix->getptr());
+
+ // Output to the log file the cpu settings
+ // This will by handy for bug reports
+ BX_INFO(("Bochs x86 Emulator %s", VER_STRING));
+ BX_INFO((" %s", REL_STRING));
+ BX_INFO(("System configuration"));
+ BX_INFO((" processors: %d",BX_SMP_PROCESSORS));
+ BX_INFO((" A20 line support: %s",BX_SUPPORT_A20?"yes":"no"));
+ BX_INFO((" APIC support: %s",BX_SUPPORT_APIC?"yes":"no"));
+
+#ifndef BX_USE_VMX
+ BX_INFO(("CPU configuration"));
+ BX_INFO((" level: %d",BX_CPU_LEVEL));
+ BX_INFO((" fpu support: %s",BX_SUPPORT_FPU?"yes":"no"));
+ BX_INFO((" paging support: %s, tlb enabled: %s",BX_SUPPORT_PAGING?"yes":"no",BX_USE_TLB?"yes":"no"));
+ BX_INFO((" mmx support: %s",BX_SUPPORT_MMX?"yes":"no"));
+ BX_INFO((" sse support: %s",BX_SUPPORT_SSE==2?"2":BX_SUPPORT_SSE==1?"1":"no"));
+ BX_INFO((" v8086 mode support: %s",BX_SUPPORT_V8086_MODE?"yes":"no"));
+ BX_INFO((" 3dnow! support: %s",BX_SUPPORT_3DNOW?"yes":"no"));
+ BX_INFO((" PAE support: %s",BX_SupportPAE?"yes":"no"));
+ BX_INFO((" PGE support: %s",BX_SupportGlobalPages?"yes":"no"));
+ BX_INFO((" PSE support: %s",BX_SUPPORT_4MEG_PAGES?"yes":"no"));
+ BX_INFO((" x86-64 support: %s",BX_SUPPORT_X86_64?"yes":"no"));
+ BX_INFO((" SEP support: %s",BX_SUPPORT_SEP?"yes":"no"));
+ BX_INFO(("Optimization configuration"));
+ BX_INFO((" Guest2HostTLB support: %s",BX_SupportGuest2HostTLB?"yes":"no"));
+ BX_INFO((" RepeatSpeedups support: %s",BX_SupportRepeatSpeedups?"yes":"no"));
+ BX_INFO((" Icache support: %s",BX_SupportICache?"yes":"no"));
+ BX_INFO((" Host Asm support: %s",BX_SupportHostAsms?"yes":"no"));
+#endif /* BX_USE_VMX */
+
+ // set up memory and CPU objects
+#if BX_SUPPORT_APIC
+ bx_generic_apic_c::reset_all_ids ();
+#endif
+
+#ifndef BX_USE_VMX
+ // Check if there is a romimage
+ if (strcmp(bx_options.rom.Opath->getptr (),"") == 0) {
+ BX_ERROR(("No romimage to load. Is your bochsrc file loaded/valid ?"));
+ }
+
+#if BX_SMP_PROCESSORS==1
+ BX_MEM(0)->init_memory(bx_options.memory.Osize->get () * 1024*1024);
+
+ // First load the optional ROM images
+ if (strcmp(bx_options.optrom[0].Opath->getptr (),"") !=0 )
+ BX_MEM(0)->load_ROM(bx_options.optrom[0].Opath->getptr (), bx_options.optrom[0].Oaddress->get (), 2);
+ if (strcmp(bx_options.optrom[1].Opath->getptr (),"") !=0 )
+ BX_MEM(0)->load_ROM(bx_options.optrom[1].Opath->getptr (), bx_options.optrom[1].Oaddress->get (), 2);
+ if (strcmp(bx_options.optrom[2].Opath->getptr (),"") !=0 )
+ BX_MEM(0)->load_ROM(bx_options.optrom[2].Opath->getptr (), bx_options.optrom[2].Oaddress->get (), 2);
+ if (strcmp(bx_options.optrom[3].Opath->getptr (),"") !=0 )
+ BX_MEM(0)->load_ROM(bx_options.optrom[3].Opath->getptr (), bx_options.optrom[3].Oaddress->get (), 2);
+
+ // Then Load the BIOS and VGABIOS
+ BX_MEM(0)->load_ROM(bx_options.rom.Opath->getptr (), bx_options.rom.Oaddress->get (), 0);
+ BX_MEM(0)->load_ROM(bx_options.vgarom.Opath->getptr (), 0xc0000, 1);
+
+ BX_CPU(0)->init (BX_MEM(0));
+ BX_CPU(0)->set_cpu_id(0);
+#if BX_SUPPORT_APIC
+ BX_CPU(0)->local_apic.set_id (0);
+#endif
+ BX_INSTR_INIT(0);
+ BX_CPU(0)->reset(BX_RESET_HARDWARE);
+#else
+ // SMP initialization
+ bx_mem_array[0] = new BX_MEM_C ();
+ bx_mem_array[0]->init_memory(bx_options.memory.Osize->get () * 1024*1024);
+
+ // First load the optional ROM images
+ if (strcmp(bx_options.optrom[0].Opath->getptr (),"") !=0 )
+ bx_mem_array[0]->load_ROM(bx_options.optrom[0].Opath->getptr (), bx_options.optrom[0].Oaddress->get (), 2);
+ if (strcmp(bx_options.optrom[1].Opath->getptr (),"") !=0 )
+ bx_mem_array[0]->load_ROM(bx_options.optrom[1].Opath->getptr (), bx_options.optrom[1].Oaddress->get (), 2);
+ if (strcmp(bx_options.optrom[2].Opath->getptr (),"") !=0 )
+ bx_mem_array[0]->load_ROM(bx_options.optrom[2].Opath->getptr (), bx_options.optrom[2].Oaddress->get (), 2);
+ if (strcmp(bx_options.optrom[3].Opath->getptr (),"") !=0 )
+ bx_mem_array[0]->load_ROM(bx_options.optrom[3].Opath->getptr (), bx_options.optrom[3].Oaddress->get (), 2);
+
+ // Then Load the BIOS and VGABIOS
+ bx_mem_array[0]->load_ROM(bx_options.rom.Opath->getptr (), bx_options.rom.Oaddress->get (), 0);
+ bx_mem_array[0]->load_ROM(bx_options.vgarom.Opath->getptr (), 0xc0000, 1);
+
+ for (int i=0; i<BX_SMP_PROCESSORS; i++) {
+ BX_CPU(i) = new BX_CPU_C;
+ BX_CPU(i)->init (bx_mem_array[0]);
+ // assign apic ID from the index of this loop
+ // if !BX_SUPPORT_APIC, this will not compile.
+ BX_CPU(i)->set_cpu_id(i);
+ BX_CPU(i)->local_apic.set_id (i);
+ BX_INSTR_INIT(i);
+ BX_CPU(i)->reset(BX_RESET_HARDWARE);
+ }
+#endif
+#else
+ // Assume UP for now for VMX
+ bx_mem.init_memory(megabytes * 1024 * 1024);
+ bx_cpu.init(&bx_mem);
+#endif // BX_USE_VMX
+
+#if BX_DEBUGGER == 0
+ DEV_init_devices();
+ DEV_reset_devices(BX_RESET_HARDWARE);
+ bx_gui->init_signal_handlers ();
+ bx_pc_system.start_timers();
+#endif
+ BX_DEBUG(("bx_init_hardware is setting signal handlers"));
+// if not using debugger, then we can take control of SIGINT.
+#if !BX_DEBUGGER
+ signal(SIGINT, bx_signal_handler);
+#endif
+
+#if BX_SHOW_IPS
+#ifndef __MINGW32__
+ signal(SIGALRM, bx_signal_handler);
+#endif
+ alarm( 1 );
+#endif
+
+ return(0);
+}
+
+
+
+ void
+bx_init_bx_dbg (void)
+{
+ bx_dbg.floppy = 0;
+ bx_dbg.keyboard = 0;
+ bx_dbg.video = 0;
+ bx_dbg.disk = 0;
+ bx_dbg.pit = 0;
+ bx_dbg.pic = 0;
+ bx_dbg.bios = 0;
+ bx_dbg.cmos = 0;
+ bx_dbg.a20 = 0;
+ bx_dbg.interrupts = 0;
+ bx_dbg.exceptions = 0;
+ bx_dbg.unsupported = 0;
+ bx_dbg.temp = 0;
+ bx_dbg.reset = 0;
+ bx_dbg.mouse = 0;
+ bx_dbg.io = 0;
+ bx_dbg.debugger = 0;
+ bx_dbg.xms = 0;
+ bx_dbg.v8086 = 0;
+ bx_dbg.paging = 0;
+ bx_dbg.creg = 0;
+ bx_dbg.dreg = 0;
+ bx_dbg.dma = 0;
+ bx_dbg.unsupported_io = 0;
+ bx_dbg.record_io = 0;
+ bx_dbg.serial = 0;
+ bx_dbg.cdrom = 0;
+#ifdef MAGIC_BREAKPOINT
+ bx_dbg.magic_break_enabled = 0;
+#endif
+
+}
+
+
+int
+bx_atexit(void)
+{
+ static bx_bool been_here = 0;
+ if (been_here) return 1; // protect from reentry
+ been_here = 1;
+
+ // in case we ended up in simulation mode, change back to config mode
+ // so that the user can see any messages left behind on the console.
+ SIM->set_display_mode (DISP_MODE_CONFIG);
+
+#if BX_PROVIDE_DEVICE_MODELS==1
+ bx_pc_system.exit();
+#endif
+
+#if BX_DEBUGGER == 0
+ if (SIM && SIM->get_init_done ()) {
+ for (int cpu=0; cpu<BX_SMP_PROCESSORS; cpu++)
+ if (BX_CPU(cpu)) BX_CPU(cpu)->atexit();
+ }
+#endif
+
+#if BX_PCI_SUPPORT
+ if (bx_options.Oi440FXSupport->get ()) {
+ bx_devices.pluginPciBridge->print_i440fx_state();
+ }
+#endif
+
+ // restore signal handling to defaults
+#if !BX_DEBUGGER
+ BX_INFO (("restoring default signal behavior"));
+ signal(SIGINT, SIG_DFL);
+#endif
+
+#if BX_SHOW_IPS
+#ifndef __MINGW32__
+ signal(SIGALRM, SIG_DFL);
+#endif
+#endif
+ return 0;
+}
+
+#if BX_PROVIDE_MAIN
+
+char *
+bx_find_bochsrc ()
+{
+ FILE *fd = NULL;
+ char rcfile[512];
+ Bit32u retry = 0, found = 0;
+ // try several possibilities for the bochsrc before giving up
+ while (!found) {
+ rcfile[0] = 0;
+ switch (retry++) {
+ case 0: strcpy (rcfile, ".bochsrc"); break;
+ case 1: strcpy (rcfile, "bochsrc"); break;
+ case 2: strcpy (rcfile, "bochsrc.txt"); break;
+#ifdef WIN32
+ case 3: strcpy (rcfile, "bochsrc.bxrc"); break;
+#elif !BX_WITH_MACOS
+ // only try this on unix
+ case 3:
+ {
+ char *ptr = getenv("HOME");
+ if (ptr) snprintf (rcfile, sizeof(rcfile), "%s/.bochsrc", ptr);
+ }
+ break;
+ case 4: strcpy (rcfile, "/etc/bochsrc"); break;
+#endif
+ default:
+ return NULL;
+ }
+ if (rcfile[0]) {
+ BX_DEBUG (("looking for configuration in %s", rcfile));
+ fd = fopen(rcfile, "r");
+ if (fd) found = 1;
+ }
+ }
+ assert (fd != NULL && rcfile[0] != 0);
+ fclose (fd);
+ return strdup (rcfile);
+}
+
+ static int
+parse_bochsrc(char *rcfile)
+{
+ FILE *fd = NULL;
+ char *ret;
+ char line[512];
+
+ // try several possibilities for the bochsrc before giving up
+
+ bochsrc_include_count++;
+
+ fd = fopen (rcfile, "r");
+ if (fd == NULL) return -1;
+
+ int retval = 0;
+ do {
+ ret = fgets(line, sizeof(line)-1, fd);
+ line[sizeof(line) - 1] = '\0';
+ int len = strlen(line);
+ if (len>0)
+ line[len-1] = '\0';
+ if ((ret != NULL) && strlen(line)) {
+ if (parse_line_unformatted(rcfile, line) < 0) {
+ retval = -1;
+ break; // quit parsing after first error
+ }
+ }
+ } while (!feof(fd));
+ fclose(fd);
+ bochsrc_include_count--;
+ return retval;
+}
+
+ static Bit32s
+parse_line_unformatted(char *context, char *line)
+{
+#define MAX_PARAMS_LEN 40
+ char *ptr;
+ unsigned i, string_i;
+ char string[512];
+ char *params[MAX_PARAMS_LEN];
+ int num_params;
+ bx_bool inquotes = 0;
+ bx_bool comment = 0;
+
+ memset(params, 0, sizeof(params));
+ if (line == NULL) return 0;
+
+ // if passed nothing but whitespace, just return
+ for (i=0; i<strlen(line); i++) {
+ if (!isspace(line[i])) break;
+ }
+ if (i>=strlen(line))
+ return 0;
+
+ num_params = 0;
+
+ if (!strncmp(line, "#include", 8))
+ ptr = strtok(line, " ");
+ else
+ ptr = strtok(line, ":");
+ while ((ptr) && (!comment)) {
+ string_i = 0;
+ for (i=0; i<strlen(ptr); i++) {
+ if (ptr[i] == '"')
+ inquotes = !inquotes;
+ else if ((ptr[i] == '#') && (strncmp(line+i, "#include", 8)) && !inquotes) {
+ comment = 1;
+ break;
+ } else {
+#if BX_HAVE_GETENV
+ // substitute environment variables.
+ if (ptr[i] == '$') {
+ char varname[512];
+ char *pv = varname;
+ char *value;
+ *pv = 0;
+ i++;
+ while (isalpha(ptr[i]) || ptr[i]=='_') {
+ *pv = ptr[i]; pv++; i++;
+ }
+ *pv = 0;
+ if (strlen(varname)<1 || !(value = getenv(varname))) {
+ BX_PANIC (("could not look up environment variable '%s'", varname));
+ } else {
+ // append value to the string
+ for (pv=value; *pv; pv++)
+ string[string_i++] = *pv;
+ }
+ }
+#endif
+ if (!isspace(ptr[i]) || inquotes) {
+ string[string_i++] = ptr[i];
+ }
+ }
+ }
+ string[string_i] = '\0';
+ if (string_i == 0) break;
+ if ( params[num_params] != NULL )
+ {
+ free(params[num_params]);
+ params[num_params] = NULL;
+ }
+ if ( num_params < MAX_PARAMS_LEN )
+ {
+ params[num_params++] = strdup (string);
+ ptr = strtok(NULL, ",");
+ }
+ else
+ {
+ BX_PANIC (("too many parameters, max is %d\n", MAX_PARAMS_LEN));
+ }
+ }
+ Bit32s retval = parse_line_formatted(context, num_params, &params[0]);
+ for (i=0; i < MAX_PARAMS_LEN; i++)
+ {
+ if ( params[i] != NULL )
+ {
+ free(params[i]);
+ params[i] = NULL;
+ }
+ }
+ return retval;
+}
+
+// These macros are called for all parse errors, so that we can easily
+// change the behavior of all occurrences.
+#define PARSE_ERR(x) \
+ do { BX_PANIC(x); return -1; } while (0)
+#define PARSE_WARN(x) \
+ BX_ERROR(x)
+
+ static Bit32s
+parse_line_formatted(char *context, int num_params, char *params[])
+{
+ int i;
+
+ if (num_params < 1) return 0;
+ if (num_params < 2) {
+ PARSE_ERR(("%s: a bochsrc option needs at least one parameter", context));
+ }
+
+ if (!strcmp(params[0], "#include")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: ignoring malformed #include directive.", context));
+ }
+ if (!strcmp(params[1], context)) {
+ PARSE_ERR(("%s: cannot include this file again.", context));
+ }
+ if (bochsrc_include_count == 2) {
+ PARSE_ERR(("%s: include directive in an included file not supported yet.", context));
+ }
+ bx_read_configuration(params[1]);
+ }
+ else if (!strcmp(params[0], "floppya")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "2_88=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_2_88);
+ }
+ else if (!strncmp(params[i], "1_44=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_1_44);
+ }
+ else if (!strncmp(params[i], "1_2=", 4)) {
+ bx_options.floppya.Opath->set (&params[i][4]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_1_2);
+ }
+ else if (!strncmp(params[i], "720k=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_720K);
+ }
+ else if (!strncmp(params[i], "360k=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_360K);
+ }
+ // use CMOS reserved types?
+ else if (!strncmp(params[i], "160k=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_160K);
+ }
+ else if (!strncmp(params[i], "180k=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_180K);
+ }
+ else if (!strncmp(params[i], "320k=", 5)) {
+ bx_options.floppya.Opath->set (&params[i][5]);
+ bx_options.floppya.Otype->set (BX_FLOPPY_320K);
+ }
+ else if (!strncmp(params[i], "status=ejected", 14)) {
+ bx_options.floppya.Ostatus->set (BX_EJECTED);
+ }
+ else if (!strncmp(params[i], "status=inserted", 15)) {
+ bx_options.floppya.Ostatus->set (BX_INSERTED);
+ }
+ else {
+ PARSE_ERR(("%s: floppya attribute '%s' not understood.", context,
+ params[i]));
+ }
+ }
+ }
+ else if (!strcmp(params[0], "gdbstub_port"))
+ {
+ if (num_params != 2)
+ {
+ fprintf(stderr, ".bochsrc: gdbstub_port directive: wrong # args.\n");
+ exit(1);
+ }
+ bx_options.gdbstub.port = atoi(params[1]);
+ }
+ else if (!strcmp(params[0], "gdbstub_text_base"))
+ {
+ if (num_params != 2)
+ {
+ fprintf(stderr, ".bochsrc: gdbstub_text_base directive: wrong # args.\n");
+ exit(1);
+ }
+ bx_options.gdbstub.text_base = atoi(params[1]);
+ }
+ else if (!strcmp(params[0], "gdbstub_data_base"))
+ {
+ if (num_params != 2)
+ {
+ fprintf(stderr, ".bochsrc: gdbstub_data_base directive: wrong # args.\n");
+ exit(1);
+ }
+ bx_options.gdbstub.data_base = atoi(params[1]);
+ }
+ else if (!strcmp(params[0], "gdbstub_bss_base"))
+ {
+ if (num_params != 2)
+ {
+ fprintf(stderr, ".bochsrc: gdbstub_bss_base directive: wrong # args.\n");
+ exit(1);
+ }
+ bx_options.gdbstub.bss_base = atoi(params[1]);
+ }
+
+ else if (!strcmp(params[0], "floppyb")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "2_88=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_2_88);
+ }
+ else if (!strncmp(params[i], "1_44=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_1_44);
+ }
+ else if (!strncmp(params[i], "1_2=", 4)) {
+ bx_options.floppyb.Opath->set (&params[i][4]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_1_2);
+ }
+ else if (!strncmp(params[i], "720k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_720K);
+ }
+ else if (!strncmp(params[i], "360k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_360K);
+ }
+ // use CMOS reserved types?
+ else if (!strncmp(params[i], "160k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_160K);
+ }
+ else if (!strncmp(params[i], "180k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_180K);
+ }
+ else if (!strncmp(params[i], "320k=", 5)) {
+ bx_options.floppyb.Opath->set (&params[i][5]);
+ bx_options.floppyb.Otype->set (BX_FLOPPY_320K);
+ }
+ else if (!strncmp(params[i], "status=ejected", 14)) {
+ bx_options.floppyb.Ostatus->set (BX_EJECTED);
+ }
+ else if (!strncmp(params[i], "status=inserted", 15)) {
+ bx_options.floppyb.Ostatus->set (BX_INSERTED);
+ }
+ else {
+ PARSE_ERR(("%s: floppyb attribute '%s' not understood.", context,
+ params[i]));
+ }
+ }
+ }
+
+ else if ((!strncmp(params[0], "ata", 3)) && (strlen(params[0]) == 4)) {
+ Bit8u channel = params[0][3];
+
+ if ((channel < '0') || (channel > '9')) {
+ PARSE_ERR(("%s: ataX directive malformed.", context));
+ }
+ channel-='0';
+ if (channel >= BX_MAX_ATA_CHANNEL) {
+ PARSE_ERR(("%s: ataX directive malformed.", context));
+ }
+
+ if ((num_params < 2) || (num_params > 5)) {
+ PARSE_ERR(("%s: ataX directive malformed.", context));
+ }
+
+ if (strncmp(params[1], "enabled=", 8)) {
+ PARSE_ERR(("%s: ataX directive malformed.", context));
+ }
+ else {
+ bx_options.ata[channel].Opresent->set (atol(&params[1][8]));
+ }
+
+ if (num_params > 2) {
+ if (strncmp(params[2], "ioaddr1=", 8)) {
+ PARSE_ERR(("%s: ataX directive malformed.", context));
+ }
+ else {
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.ata[channel].Oioaddr1->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.ata[channel].Oioaddr1->set (strtoul (&params[2][8], NULL, 10));
+ }
+ }
+
+ if (num_params > 3) {
+ if (strncmp(params[3], "ioaddr2=", 8)) {
+ PARSE_ERR(("%s: ataX directive malformed.", context));
+ }
+ else {
+ if ( (params[3][8] == '0') && (params[3][9] == 'x') )
+ bx_options.ata[channel].Oioaddr2->set (strtoul (&params[3][8], NULL, 16));
+ else
+ bx_options.ata[channel].Oioaddr2->set (strtoul (&params[3][8], NULL, 10));
+ }
+ }
+
+ if (num_params > 4) {
+ if (strncmp(params[4], "irq=", 4)) {
+ PARSE_ERR(("%s: ataX directive malformed.", context));
+ }
+ else {
+ bx_options.ata[channel].Oirq->set (atol(&params[4][4]));
+ }
+ }
+ }
+
+ // ataX-master, ataX-slave
+ else if ((!strncmp(params[0], "ata", 3)) && (strlen(params[0]) > 4)) {
+ Bit8u channel = params[0][3], slave = 0;
+
+ if ((channel < '0') || (channel > '9')) {
+ PARSE_ERR(("%s: ataX-master/slave directive malformed.", context));
+ }
+ channel-='0';
+ if (channel >= BX_MAX_ATA_CHANNEL) {
+ PARSE_ERR(("%s: ataX-master/slave directive malformed.", context));
+ }
+
+ if ((strcmp(&params[0][4], "-slave")) &&
+ (strcmp(&params[0][4], "-master"))) {
+ PARSE_ERR(("%s: ataX-master/slave directive malformed.", context));
+ }
+
+ if (!strcmp(&params[0][4], "-slave")) {
+ slave = 1;
+ }
+
+ // This was originally meant to warn users about both diskc
+ // and ata0-master defined, but it also prevent users to
+ // override settings on the command line
+ // (see [ 661010 ] cannot override ata-settings from cmdline)
+ // if (bx_options.atadevice[channel][slave].Opresent->get()) {
+ // BX_INFO(("%s: %s device of ata channel %d already defined.", context, slave?"slave":"master",channel));
+ // }
+
+ for (i=1; i<num_params; i++) {
+ if (!strcmp(params[i], "type=disk")) {
+ bx_options.atadevice[channel][slave].Otype->set (BX_ATA_DEVICE_DISK);
+ }
+ else if (!strcmp(params[i], "type=cdrom")) {
+ bx_options.atadevice[channel][slave].Otype->set (BX_ATA_DEVICE_CDROM);
+ }
+ else if (!strcmp(params[i], "mode=flat")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_FLAT);
+ }
+ else if (!strcmp(params[i], "mode=concat")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_CONCAT);
+ }
+ else if (!strcmp(params[i], "mode=external")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_EXTDISKSIM);
+ }
+ else if (!strcmp(params[i], "mode=dll")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_DLL_HD);
+ }
+ else if (!strcmp(params[i], "mode=sparse")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_SPARSE);
+ }
+ else if (!strcmp(params[i], "mode=vmware3")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_VMWARE3);
+ }
+// else if (!strcmp(params[i], "mode=split")) {
+// bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_SPLIT);
+// }
+ else if (!strcmp(params[i], "mode=undoable")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_UNDOABLE);
+ }
+ else if (!strcmp(params[i], "mode=growing")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_GROWING);
+ }
+ else if (!strcmp(params[i], "mode=volatile")) {
+ bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_VOLATILE);
+ }
+// else if (!strcmp(params[i], "mode=z-undoable")) {
+// bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_Z_UNDOABLE);
+// }
+// else if (!strcmp(params[i], "mode=z-volatile")) {
+// bx_options.atadevice[channel][slave].Omode->set (BX_ATA_MODE_Z_VOLATILE);
+// }
+ else if (!strncmp(params[i], "path=", 5)) {
+ bx_options.atadevice[channel][slave].Opath->set (&params[i][5]);
+ }
+ else if (!strncmp(params[i], "cylinders=", 10)) {
+ bx_options.atadevice[channel][slave].Ocylinders->set (atol(&params[i][10]));
+ }
+ else if (!strncmp(params[i], "heads=", 6)) {
+ bx_options.atadevice[channel][slave].Oheads->set (atol(&params[i][6]));
+ }
+ else if (!strncmp(params[i], "spt=", 4)) {
+ bx_options.atadevice[channel][slave].Ospt->set (atol(&params[i][4]));
+ }
+ else if (!strncmp(params[i], "model=", 6)) {
+ bx_options.atadevice[channel][slave].Omodel->set(&params[i][6]);
+ }
+ else if (!strcmp(params[i], "biosdetect=none")) {
+ bx_options.atadevice[channel][slave].Obiosdetect->set(BX_ATA_BIOSDETECT_NONE);
+ }
+ else if (!strcmp(params[i], "biosdetect=cmos")) {
+ bx_options.atadevice[channel][slave].Obiosdetect->set(BX_ATA_BIOSDETECT_CMOS);
+ }
+ else if (!strcmp(params[i], "biosdetect=auto")) {
+ bx_options.atadevice[channel][slave].Obiosdetect->set(BX_ATA_BIOSDETECT_AUTO);
+ }
+ else if (!strcmp(params[i], "translation=none")) {
+ bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_NONE);
+ }
+ else if (!strcmp(params[i], "translation=lba")) {
+ bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_LBA);
+ }
+ else if (!strcmp(params[i], "translation=large")) {
+ bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_LARGE);
+ }
+ else if (!strcmp(params[i], "translation=echs")) { // synonym of large
+ bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_LARGE);
+ }
+ else if (!strcmp(params[i], "translation=rechs")) {
+ bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_RECHS);
+ }
+ else if (!strcmp(params[i], "translation=auto")) {
+ bx_options.atadevice[channel][slave].Otranslation->set(BX_ATA_TRANSLATION_AUTO);
+ }
+ else if (!strcmp(params[i], "status=ejected")) {
+ bx_options.atadevice[channel][slave].Ostatus->set(BX_EJECTED);
+ }
+ else if (!strcmp(params[i], "status=inserted")) {
+ bx_options.atadevice[channel][slave].Ostatus->set(BX_INSERTED);
+ }
+ else if (!strncmp(params[i], "journal=", 8)) {
+ bx_options.atadevice[channel][slave].Ojournal->set(&params[i][8]);
+ }
+ else {
+ PARSE_ERR(("%s: ataX-master/slave directive malformed.", context));
+ }
+ }
+
+ // Enables the ata device
+ bx_options.atadevice[channel][slave].Opresent->set(1);
+
+ // if enabled, check if device ok
+ if (bx_options.atadevice[channel][slave].Opresent->get() == 1) {
+ if (bx_options.atadevice[channel][slave].Otype->get() == BX_ATA_DEVICE_DISK) {
+ if (strlen(bx_options.atadevice[channel][slave].Opath->getptr()) ==0)
+ PARSE_WARN(("%s: ataX-master/slave has empty path", context));
+ if ((bx_options.atadevice[channel][slave].Ocylinders->get() == 0) ||
+ (bx_options.atadevice[channel][slave].Oheads->get() ==0 ) ||
+ (bx_options.atadevice[channel][slave].Ospt->get() == 0)) {
+ PARSE_WARN(("%s: ataX-master/slave cannot have zero cylinders, heads, or sectors/track", context));
+ }
+ }
+ else if (bx_options.atadevice[channel][slave].Otype->get() == BX_ATA_DEVICE_CDROM) {
+ if (strlen(bx_options.atadevice[channel][slave].Opath->getptr()) == 0) {
+ PARSE_WARN(("%s: ataX-master/slave has empty path", context));
+ }
+ }
+ else {
+ PARSE_WARN(("%s: ataX-master/slave: type should be specified", context));
+ }
+ }
+ }
+
+ // Legacy disk options emulation
+ else if (!strcmp(params[0], "diskc")) { // DEPRECATED
+ BX_INFO(("WARNING: diskc directive is deprecated, use ata0-master: instead"));
+ if (bx_options.atadevice[0][0].Opresent->get()) {
+ PARSE_ERR(("%s: master device of ata channel 0 already defined.", context));
+ }
+ if (num_params != 5) {
+ PARSE_ERR(("%s: diskc directive malformed.", context));
+ }
+ if (strncmp(params[1], "file=", 5) ||
+ strncmp(params[2], "cyl=", 4) ||
+ strncmp(params[3], "heads=", 6) ||
+ strncmp(params[4], "spt=", 4)) {
+ PARSE_ERR(("%s: diskc directive malformed.", context));
+ }
+ bx_options.ata[0].Opresent->set(1);
+ bx_options.atadevice[0][0].Otype->set (BX_ATA_DEVICE_DISK);
+ bx_options.atadevice[0][0].Opath->set (&params[1][5]);
+ bx_options.atadevice[0][0].Ocylinders->set (atol(&params[2][4]));
+ bx_options.atadevice[0][0].Oheads->set (atol(&params[3][6]));
+ bx_options.atadevice[0][0].Ospt->set (atol(&params[4][4]));
+ bx_options.atadevice[0][0].Opresent->set (1);
+ }
+ else if (!strcmp(params[0], "diskd")) { // DEPRECATED
+ BX_INFO(("WARNING: diskd directive is deprecated, use ata0-slave: instead"));
+ if (bx_options.atadevice[0][1].Opresent->get()) {
+ PARSE_ERR(("%s: slave device of ata channel 0 already defined.", context));
+ }
+ if (num_params != 5) {
+ PARSE_ERR(("%s: diskd directive malformed.", context));
+ }
+ if (strncmp(params[1], "file=", 5) ||
+ strncmp(params[2], "cyl=", 4) ||
+ strncmp(params[3], "heads=", 6) ||
+ strncmp(params[4], "spt=", 4)) {
+ PARSE_ERR(("%s: diskd directive malformed.", context));
+ }
+ bx_options.ata[0].Opresent->set(1);
+ bx_options.atadevice[0][1].Otype->set (BX_ATA_DEVICE_DISK);
+ bx_options.atadevice[0][1].Opath->set (&params[1][5]);
+ bx_options.atadevice[0][1].Ocylinders->set (atol( &params[2][4]));
+ bx_options.atadevice[0][1].Oheads->set (atol( &params[3][6]));
+ bx_options.atadevice[0][1].Ospt->set (atol( &params[4][4]));
+ bx_options.atadevice[0][1].Opresent->set (1);
+ }
+ else if (!strcmp(params[0], "cdromd")) { // DEPRECATED
+ BX_INFO(("WARNING: cdromd directive is deprecated, use ata0-slave: instead"));
+ if (bx_options.atadevice[0][1].Opresent->get()) {
+ PARSE_ERR(("%s: slave device of ata channel 0 already defined.", context));
+ }
+ if (num_params != 3) {
+ PARSE_ERR(("%s: cdromd directive malformed.", context));
+ }
+ if (strncmp(params[1], "dev=", 4) || strncmp(params[2], "status=", 7)) {
+ PARSE_ERR(("%s: cdromd directive malformed.", context));
+ }
+ bx_options.ata[0].Opresent->set(1);
+ bx_options.atadevice[0][1].Otype->set (BX_ATA_DEVICE_CDROM);
+ bx_options.atadevice[0][1].Opath->set (&params[1][4]);
+ if (!strcmp(params[2], "status=inserted"))
+ bx_options.atadevice[0][1].Ostatus->set (BX_INSERTED);
+ else if (!strcmp(params[2], "status=ejected"))
+ bx_options.atadevice[0][1].Ostatus->set (BX_EJECTED);
+ else {
+ PARSE_ERR(("%s: cdromd directive malformed.", context));
+ }
+ bx_options.atadevice[0][1].Opresent->set (1);
+ }
+
+ else if (!strcmp(params[0], "boot")) {
+ if (!strcmp(params[1], "a")) {
+ bx_options.Obootdrive->set (BX_BOOT_FLOPPYA);
+ } else if (!strcmp(params[1], "floppy")) {
+ bx_options.Obootdrive->set (BX_BOOT_FLOPPYA);
+ } else if (!strcmp(params[1], "c")) {
+ bx_options.Obootdrive->set (BX_BOOT_DISKC);
+ } else if (!strcmp(params[1], "disk")) {
+ bx_options.Obootdrive->set (BX_BOOT_DISKC);
+ } else if (!strcmp(params[1], "cdrom")) {
+ bx_options.Obootdrive->set (BX_BOOT_CDROM);
+ } else {
+ PARSE_ERR(("%s: boot directive with unknown boot device '%s'. use 'floppy', 'disk' or 'cdrom'.", context, params[1]));
+ }
+ }
+
+ else if (!strcmp(params[0], "com1")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.com[0].Oenabled->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[0].Odev->set (&params[i][4]);
+ bx_options.com[0].Oenabled->set (1);
+ }
+ else {
+ PARSE_ERR(("%s: unknown parameter for com1 ignored.", context));
+ }
+ }
+ }
+#if 0
+ else if (!strcmp(params[0], "com2")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.com[1].Oenabled->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[1].Odev->set (&params[i][4]);
+ bx_options.com[1].Oenabled->set (1);
+ }
+ else {
+ PARSE_ERR(("%s: unknown parameter for com2 ignored.", context));
+ }
+ }
+ }
+ else if (!strcmp(params[0], "com3")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.com[2].Oenabled->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[2].Odev->set (&params[i][4]);
+ bx_options.com[2].Oenabled->set (1);
+ }
+ else {
+ PARSE_ERR(("%s: unknown parameter for com3 ignored.", context));
+ }
+ }
+ }
+ else if (!strcmp(params[0], "com4")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.com[3].Oenabled->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "dev=", 4)) {
+ bx_options.com[3].Odev->set (&params[i][4]);
+ bx_options.com[3].Oenabled->set (1);
+ }
+ else {
+ PARSE_ERR(("%s: unknown parameter for com4 ignored.", context));
+ }
+ }
+ }
+#endif
+ else if (!strcmp(params[0], "usb1")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.usb[0].Oenabled->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "ioaddr=", 7)) {
+ if ( (params[i][7] == '0') && (params[i][8] == 'x') )
+ bx_options.usb[0].Oioaddr->set (strtoul (&params[i][7], NULL, 16));
+ else
+ bx_options.usb[0].Oioaddr->set (strtoul (&params[i][7], NULL, 10));
+ bx_options.usb[0].Oenabled->set (1);
+ }
+ else if (!strncmp(params[i], "irq=", 4)) {
+ bx_options.usb[0].Oirq->set (atol(&params[i][4]));
+ }
+ else {
+ PARSE_ERR(("%s: unknown parameter for usb1 ignored.", context));
+ }
+ }
+ }
+ else if (!strcmp(params[0], "floppy_bootsig_check")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: floppy_bootsig_check directive malformed.", context));
+ }
+ if (strncmp(params[1], "disabled=", 9)) {
+ PARSE_ERR(("%s: floppy_bootsig_check directive malformed.", context));
+ }
+ if (params[1][9] == '0')
+ bx_options.OfloppySigCheck->set (0);
+ else if (params[1][9] == '1')
+ bx_options.OfloppySigCheck->set (1);
+ else {
+ PARSE_ERR(("%s: floppy_bootsig_check directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "log")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: log directive has wrong # args.", context));
+ }
+ bx_options.log.Ofilename->set (params[1]);
+ }
+ else if (!strcmp(params[0], "logprefix")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: logprefix directive has wrong # args.", context));
+ }
+ bx_options.log.Oprefix->set (params[1]);
+ }
+ else if (!strcmp(params[0], "debugger_log")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: debugger_log directive has wrong # args.", context));
+ }
+ bx_options.log.Odebugger_filename->set (params[1]);
+ }
+ else if (!strcmp(params[0], "panic")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: panic directive malformed.", context));
+ }
+ if (strncmp(params[1], "action=", 7)) {
+ PARSE_ERR(("%s: panic directive malformed.", context));
+ }
+ char *action = 7 + params[1];
+ if (!strcmp(action, "fatal"))
+ SIM->set_default_log_action (LOGLEV_PANIC, ACT_FATAL);
+ else if (!strcmp (action, "report"))
+ SIM->set_default_log_action (LOGLEV_PANIC, ACT_REPORT);
+ else if (!strcmp (action, "ignore"))
+ SIM->set_default_log_action (LOGLEV_PANIC, ACT_IGNORE);
+ else if (!strcmp (action, "ask"))
+ SIM->set_default_log_action (LOGLEV_PANIC, ACT_ASK);
+ else {
+ PARSE_ERR(("%s: panic directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "pass")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: pass directive malformed.", context));
+ }
+ if (strncmp(params[1], "action=", 7)) {
+ PARSE_ERR(("%s: pass directive malformed.", context));
+ }
+ char *action = 7 + params[1];
+ if (!strcmp(action, "fatal"))
+ SIM->set_default_log_action (LOGLEV_PASS, ACT_FATAL);
+ else if (!strcmp (action, "report"))
+ SIM->set_default_log_action (LOGLEV_PASS, ACT_REPORT);
+ else if (!strcmp (action, "ignore"))
+ SIM->set_default_log_action (LOGLEV_PASS, ACT_IGNORE);
+ else if (!strcmp (action, "ask"))
+ SIM->set_default_log_action (LOGLEV_PASS, ACT_ASK);
+ else {
+ PARSE_ERR(("%s: pass directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "error")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: error directive malformed.", context));
+ }
+ if (strncmp(params[1], "action=", 7)) {
+ PARSE_ERR(("%s: error directive malformed.", context));
+ }
+ char *action = 7 + params[1];
+ if (!strcmp(action, "fatal"))
+ SIM->set_default_log_action (LOGLEV_ERROR, ACT_FATAL);
+ else if (!strcmp (action, "report"))
+ SIM->set_default_log_action (LOGLEV_ERROR, ACT_REPORT);
+ else if (!strcmp (action, "ignore"))
+ SIM->set_default_log_action (LOGLEV_ERROR, ACT_IGNORE);
+ else if (!strcmp (action, "ask"))
+ SIM->set_default_log_action (LOGLEV_ERROR, ACT_ASK);
+ else {
+ PARSE_ERR(("%s: error directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "info")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: info directive malformed.", context));
+ }
+ if (strncmp(params[1], "action=", 7)) {
+ PARSE_ERR(("%s: info directive malformed.", context));
+ }
+ char *action = 7 + params[1];
+ if (!strcmp(action, "fatal"))
+ SIM->set_default_log_action (LOGLEV_INFO, ACT_FATAL);
+ else if (!strcmp (action, "report"))
+ SIM->set_default_log_action (LOGLEV_INFO, ACT_REPORT);
+ else if (!strcmp (action, "ignore"))
+ SIM->set_default_log_action (LOGLEV_INFO, ACT_IGNORE);
+ else if (!strcmp (action, "ask"))
+ SIM->set_default_log_action (LOGLEV_INFO, ACT_ASK);
+ else {
+ PARSE_ERR(("%s: info directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "debug")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: debug directive malformed.", context));
+ }
+ if (strncmp(params[1], "action=", 7)) {
+ PARSE_ERR(("%s: debug directive malformed.", context));
+ }
+ char *action = 7 + params[1];
+ if (!strcmp(action, "fatal"))
+ SIM->set_default_log_action (LOGLEV_DEBUG, ACT_FATAL);
+ else if (!strcmp (action, "report"))
+ SIM->set_default_log_action (LOGLEV_DEBUG, ACT_REPORT);
+ else if (!strcmp (action, "ignore"))
+ SIM->set_default_log_action (LOGLEV_DEBUG, ACT_IGNORE);
+ else if (!strcmp (action, "ask"))
+ SIM->set_default_log_action (LOGLEV_DEBUG, ACT_ASK);
+ else {
+ PARSE_ERR(("%s: debug directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "romimage")) {
+ if (num_params != 3) {
+ PARSE_ERR(("%s: romimage directive: wrong # args.", context));
+ }
+ if (strncmp(params[1], "file=", 5)) {
+ PARSE_ERR(("%s: romimage directive malformed.", context));
+ }
+ if (strncmp(params[2], "address=", 8)) {
+ PARSE_ERR(("%s: romimage directive malformed.", context));
+ }
+ bx_options.rom.Opath->set (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.rom.Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.rom.Oaddress->set (strtoul (&params[2][8], NULL, 10));
+ }
+ else if (!strcmp(params[0], "optromimage1")) {
+ if (num_params != 3) {
+ PARSE_ERR(("%s: optromimage1 directive: wrong # args.", context));
+ }
+ if (strncmp(params[1], "file=", 5)) {
+ PARSE_ERR(("%s: optromimage1 directive malformed.", context));
+ }
+ if (strncmp(params[2], "address=", 8)) {
+ PARSE_ERR(("%s: optromimage2 directive malformed.", context));
+ }
+ bx_options.optrom[0].Opath->set (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[0].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[0].Oaddress->set (strtoul (&params[2][8], NULL, 10));
+ }
+ else if (!strcmp(params[0], "optromimage2")) {
+ if (num_params != 3) {
+ PARSE_ERR(("%s: optromimage2 directive: wrong # args.", context));
+ }
+ if (strncmp(params[1], "file=", 5)) {
+ PARSE_ERR(("%s: optromimage2 directive malformed.", context));
+ }
+ if (strncmp(params[2], "address=", 8)) {
+ PARSE_ERR(("%s: optromimage2 directive malformed.", context));
+ }
+ bx_options.optrom[1].Opath->set (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[1].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[1].Oaddress->set (strtoul (&params[2][8], NULL, 10));
+ }
+ else if (!strcmp(params[0], "optromimage3")) {
+ if (num_params != 3) {
+ PARSE_ERR(("%s: optromimage3 directive: wrong # args.", context));
+ }
+ if (strncmp(params[1], "file=", 5)) {
+ PARSE_ERR(("%s: optromimage3 directive malformed.", context));
+ }
+ if (strncmp(params[2], "address=", 8)) {
+ PARSE_ERR(("%s: optromimage2 directive malformed.", context));
+ }
+ bx_options.optrom[2].Opath->set (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[2].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[2].Oaddress->set (strtoul (&params[2][8], NULL, 10));
+ }
+ else if (!strcmp(params[0], "optromimage4")) {
+ if (num_params != 3) {
+ PARSE_ERR(("%s: optromimage4 directive: wrong # args.", context));
+ }
+ if (strncmp(params[1], "file=", 5)) {
+ PARSE_ERR(("%s: optromimage4 directive malformed.", context));
+ }
+ if (strncmp(params[2], "address=", 8)) {
+ PARSE_ERR(("%s: optromimage2 directive malformed.", context));
+ }
+ bx_options.optrom[3].Opath->set (&params[1][5]);
+ if ( (params[2][8] == '0') && (params[2][9] == 'x') )
+ bx_options.optrom[3].Oaddress->set (strtoul (&params[2][8], NULL, 16));
+ else
+ bx_options.optrom[3].Oaddress->set (strtoul (&params[2][8], NULL, 10));
+ }
+ else if (!strcmp(params[0], "vgaromimage")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: vgaromimage directive: wrong # args.", context));
+ }
+ bx_options.vgarom.Opath->set (params[1]);
+ }
+ else if (!strcmp(params[0], "vga_update_interval")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: vga_update_interval directive: wrong # args.", context));
+ }
+ bx_options.Ovga_update_interval->set (atol(params[1]));
+ if (bx_options.Ovga_update_interval->get () < 50000) {
+ BX_INFO(("%s: vga_update_interval seems awfully small!", context));
+ }
+ }
+ else if (!strcmp(params[0], "keyboard_serial_delay")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: keyboard_serial_delay directive: wrong # args.", context));
+ }
+ bx_options.Okeyboard_serial_delay->set (atol(params[1]));
+ if (bx_options.Okeyboard_serial_delay->get () < 5) {
+ PARSE_ERR (("%s: keyboard_serial_delay not big enough!", context));
+ }
+ }
+ else if (!strcmp(params[0], "keyboard_paste_delay")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: keyboard_paste_delay directive: wrong # args.", context));
+ }
+ bx_options.Okeyboard_paste_delay->set (atol(params[1]));
+ if (bx_options.Okeyboard_paste_delay->get () < 1000) {
+ PARSE_ERR (("%s: keyboard_paste_delay not big enough!", context));
+ }
+ }
+ else if (!strcmp(params[0], "megs")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: megs directive: wrong # args.", context));
+ }
+ bx_options.memory.Osize->set (atol(params[1]));
+ }
+ else if (!strcmp(params[0], "floppy_command_delay")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: floppy_command_delay directive: wrong # args.", context));
+ }
+ bx_options.Ofloppy_command_delay->set (atol(params[1]));
+ if (bx_options.Ofloppy_command_delay->get () < 100) {
+ PARSE_ERR(("%s: floppy_command_delay not big enough!", context));
+ }
+ }
+ else if (!strcmp(params[0], "ips")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: ips directive: wrong # args.", context));
+ }
+ bx_options.Oips->set (atol(params[1]));
+ if (bx_options.Oips->get () < BX_MIN_IPS) {
+ BX_ERROR(("%s: WARNING: ips is AWFULLY low!", context));
+ }
+ }
+ else if (!strcmp(params[0], "pit")) { // Deprecated
+ if (num_params != 2) {
+ PARSE_ERR(("%s: pit directive: wrong # args.", context));
+ }
+ BX_INFO(("WARNING: pit directive is deprecated, use clock: instead"));
+ if (!strncmp(params[1], "realtime=", 9)) {
+ switch (params[1][9]) {
+ case '0':
+ BX_INFO(("WARNING: not disabling realtime pit"));
+ break;
+ case '1': bx_options.clock.Osync->set (BX_CLOCK_SYNC_REALTIME); break;
+ default: PARSE_ERR(("%s: pit expected realtime=[0|1] arg", context));
+ }
+ }
+ else PARSE_ERR(("%s: pit expected realtime=[0|1] arg", context));
+ }
+ else if (!strcmp(params[0], "max_ips")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: max_ips directive: wrong # args.", context));
+ }
+ BX_INFO(("WARNING: max_ips not implemented"));
+ }
+ else if (!strcmp(params[0], "text_snapshot_check")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: text_snapshot_check directive: wrong # args.", context));
+ }
+ if (!strncmp(params[1], "enable", 6)) {
+ bx_options.Otext_snapshot_check->set (1);
+ }
+ else if (!strncmp(params[1], "disable", 7)) {
+ bx_options.Otext_snapshot_check->set (0);
+ }
+ else bx_options.Otext_snapshot_check->set (!!(atol(params[1])));
+ }
+ else if (!strcmp(params[0], "mouse")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: mouse directive malformed.", context));
+ }
+ if (strncmp(params[1], "enabled=", 8)) {
+ PARSE_ERR(("%s: mouse directive malformed.", context));
+ }
+ if (params[1][8] == '0' || params[1][8] == '1')
+ bx_options.Omouse_enabled->set (params[1][8] - '0');
+ else
+ PARSE_ERR(("%s: mouse directive malformed.", context));
+ }
+ else if (!strcmp(params[0], "private_colormap")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: private_colormap directive malformed.", context));
+ }
+ if (strncmp(params[1], "enabled=", 8)) {
+ PARSE_ERR(("%s: private_colormap directive malformed.", context));
+ }
+ if (params[1][8] == '0' || params[1][8] == '1')
+ bx_options.Oprivate_colormap->set (params[1][8] - '0');
+ else {
+ PARSE_ERR(("%s: private_colormap directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "fullscreen")) {
+#if BX_WITH_AMIGAOS
+ if (num_params != 2) {
+ PARSE_ERR(("%s: fullscreen directive malformed.", context));
+ }
+ if (strncmp(params[1], "enabled=", 8)) {
+ PARSE_ERR(("%s: fullscreen directive malformed.", context));
+ }
+ if (params[1][8] == '0' || params[1][8] == '1') {
+ bx_options.Ofullscreen->set (params[1][8] - '0');
+ } else {
+ PARSE_ERR(("%s: fullscreen directive malformed.", context));
+ }
+#endif
+ }
+ else if (!strcmp(params[0], "screenmode")) {
+#if BX_WITH_AMIGAOS
+ if (num_params != 2) {
+ PARSE_ERR(("%s: screenmode directive malformed.", context));
+ }
+ if (strncmp(params[1], "name=", 5)) {
+ PARSE_ERR(("%s: screenmode directive malformed.", context));
+ }
+ bx_options.Oscreenmode->set (strdup(&params[1][5]));
+#endif
+ }
+
+ else if (!strcmp(params[0], "sb16")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "midi=", 5)) {
+ bx_options.sb16.Omidifile->set (strdup(&params[i][5]));
+ }
+ else if (!strncmp(params[i], "midimode=", 9)) {
+ bx_options.sb16.Omidimode->set (atol(&params[i][9]));
+ }
+ else if (!strncmp(params[i], "wave=", 5)) {
+ bx_options.sb16.Owavefile->set (strdup(&params[i][5]));
+ }
+ else if (!strncmp(params[i], "wavemode=", 9)) {
+ bx_options.sb16.Owavemode->set (atol(&params[i][9]));
+ }
+ else if (!strncmp(params[i], "log=", 4)) {
+ bx_options.sb16.Ologfile->set (strdup(&params[i][4]));
+ }
+ else if (!strncmp(params[i], "loglevel=", 9)) {
+ bx_options.sb16.Ologlevel->set (atol(&params[i][9]));
+ }
+ else if (!strncmp(params[i], "dmatimer=", 9)) {
+ bx_options.sb16.Odmatimer->set (atol(&params[i][9]));
+ }
+ }
+ if (bx_options.sb16.Odmatimer->get () > 0)
+ bx_options.sb16.Opresent->set (1);
+ }
+
+ else if (!strcmp(params[0], "parport1")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.par[0].Oenabled->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "file=", 5)) {
+ bx_options.par[0].Ooutfile->set (strdup(&params[i][5]));
+ bx_options.par[0].Oenabled->set (1);
+ }
+ else {
+ BX_ERROR(("%s: unknown parameter for parport1 ignored.", context));
+ }
+ }
+ }
+
+#if 0
+ else if (!strcmp(params[0], "parport2")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.par[1].Oenabled->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "file=", 5)) {
+ bx_options.par[1].Ooutfile->set (strdup(&params[i][5]));
+ bx_options.par[1].Oenabled->set (1);
+ }
+ else {
+ BX_ERROR(("%s: unknown parameter for parport2 ignored.", context));
+ }
+ }
+ }
+#endif
+
+ else if (!strcmp(params[0], "i440fxsupport")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: i440FXSupport directive malformed.", context));
+ }
+ if (strncmp(params[1], "enabled=", 8)) {
+ PARSE_ERR(("%s: i440FXSupport directive malformed.", context));
+ }
+ if (params[1][8] == '0')
+ bx_options.Oi440FXSupport->set (0);
+ else if (params[1][8] == '1')
+ bx_options.Oi440FXSupport->set (1);
+ else {
+ PARSE_ERR(("%s: i440FXSupport directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "newharddrivesupport")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: newharddrivesupport directive malformed.", context));
+ }
+ if (strncmp(params[1], "enabled=", 8)) {
+ PARSE_ERR(("%s: newharddrivesupport directive malformed.", context));
+ }
+ if (params[1][8] == '0')
+ bx_options.OnewHardDriveSupport->set (0);
+ else if (params[1][8] == '1')
+ bx_options.OnewHardDriveSupport->set (1);
+ else {
+ PARSE_ERR(("%s: newharddrivesupport directive malformed.", context));
+ }
+ }
+ else if (!strcmp(params[0], "cmosimage")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: cmosimage directive: wrong # args.", context));
+ }
+ bx_options.cmos.Opath->set (strdup(params[1]));
+ bx_options.cmos.OcmosImage->set (1); // CMOS Image is true
+ }
+ else if (!strcmp(params[0], "time0")) { // Deprectated
+ BX_INFO(("WARNING: time0 directive is deprecated, use clock: instead"));
+ if (num_params != 2) {
+ PARSE_ERR(("%s: time0 directive: wrong # args.", context));
+ }
+ bx_options.clock.Otime0->set (atoi(params[1]));
+ }
+ else if (!strcmp(params[0], "clock")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "sync=", 5)) {
+ bx_options.clock.Osync->set_by_name (&params[i][5]);
+ }
+ else if (!strcmp(params[i], "time0=local")) {
+ bx_options.clock.Otime0->set (BX_CLOCK_TIME0_LOCAL);
+ }
+ else if (!strcmp(params[i], "time0=utc")) {
+ bx_options.clock.Otime0->set (BX_CLOCK_TIME0_UTC);
+ }
+ else if (!strncmp(params[i], "time0=", 6)) {
+ bx_options.clock.Otime0->set (atoi(&params[i][6]));
+ }
+ else {
+ BX_ERROR(("%s: unknown parameter for clock ignored.", context));
+ }
+ }
+ }
+#ifdef MAGIC_BREAKPOINT
+ else if (!strcmp(params[0], "magic_break")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: magic_break directive: wrong # args.", context));
+ }
+ if (strncmp(params[1], "enabled=", 8)) {
+ PARSE_ERR(("%s: magic_break directive malformed.", context));
+ }
+ if (params[1][8] == '0') {
+ BX_INFO(("Ignoring magic break points"));
+ bx_dbg.magic_break_enabled = 0;
+ }
+ else if (params[1][8] == '1') {
+ BX_INFO(("Stopping on magic break points"));
+ bx_dbg.magic_break_enabled = 1;
+ }
+ else {
+ PARSE_ERR(("%s: magic_break directive malformed.", context));
+ }
+ }
+#endif
+ else if (!strcmp(params[0], "ne2k")) {
+ int tmp[6];
+ char tmpchar[6];
+ int valid = 0;
+ int n;
+ if (!bx_options.ne2k.Opresent->get ()) {
+ bx_options.ne2k.Oethmod->set_by_name ("null");
+ }
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "ioaddr=", 7)) {
+ bx_options.ne2k.Oioaddr->set (strtoul(&params[i][7], NULL, 16));
+ valid |= 0x01;
+ }
+ else if (!strncmp(params[i], "irq=", 4)) {
+ bx_options.ne2k.Oirq->set (atol(&params[i][4]));
+ valid |= 0x02;
+ }
+ else if (!strncmp(params[i], "mac=", 4)) {
+ n = sscanf(&params[i][4], "%x:%x:%x:%x:%x:%x",
+ &tmp[0],&tmp[1],&tmp[2],&tmp[3],&tmp[4],&tmp[5]);
+ if (n != 6) {
+ PARSE_ERR(("%s: ne2k mac address malformed.", context));
+ }
+ for (n=0;n<6;n++)
+ tmpchar[n] = (unsigned char)tmp[n];
+ bx_options.ne2k.Omacaddr->set (tmpchar);
+ valid |= 0x04;
+ }
+ else if (!strncmp(params[i], "ethmod=", 7)) {
+ if (!bx_options.ne2k.Oethmod->set_by_name (strdup(&params[i][7])))
+ PARSE_ERR(("%s: ethernet module '%s' not available", context, strdup(&params[i][7])));
+ }
+ else if (!strncmp(params[i], "ethdev=", 7)) {
+ bx_options.ne2k.Oethdev->set (strdup(&params[i][7]));
+ }
+ else if (!strncmp(params[i], "script=", 7)) {
+ bx_options.ne2k.Oscript->set (strdup(&params[i][7]));
+ }
+ else {
+ PARSE_ERR(("%s: ne2k directive malformed.", context));
+ }
+ }
+ if (!bx_options.ne2k.Opresent->get ()) {
+ if (valid == 0x07) {
+ bx_options.ne2k.Opresent->set (1);
+ }
+ else {
+ PARSE_ERR(("%s: ne2k directive incomplete (ioaddr, irq and mac are required)", context));
+ }
+ }
+ }
+
+ else if (!strcmp(params[0], "load32bitOSImage")) {
+ if ( (num_params!=4) && (num_params!=5) ) {
+ PARSE_ERR(("%s: load32bitOSImage directive: wrong # args.", context));
+ }
+ if (strncmp(params[1], "os=", 3)) {
+ PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context));
+ }
+ if (!strcmp(&params[1][3], "nullkernel")) {
+ bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSNullKernel);
+ }
+ else if (!strcmp(&params[1][3], "linux")) {
+ bx_options.load32bitOSImage.OwhichOS->set (Load32bitOSLinux);
+ }
+ else {
+ PARSE_ERR(("%s: load32bitOSImage: unsupported OS.", context));
+ }
+ if (strncmp(params[2], "path=", 5)) {
+ PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context));
+ }
+ if (strncmp(params[3], "iolog=", 6)) {
+ PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context));
+ }
+ bx_options.load32bitOSImage.Opath->set (strdup(&params[2][5]));
+ bx_options.load32bitOSImage.Oiolog->set (strdup(&params[3][6]));
+ if (num_params == 5) {
+ if (strncmp(params[4], "initrd=", 7)) {
+ PARSE_ERR(("%s: load32bitOSImage: directive malformed.", context));
+ }
+ bx_options.load32bitOSImage.Oinitrd->set (strdup(&params[4][7]));
+ }
+ }
+ else if (!strcmp(params[0], "keyboard_type")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: keyboard_type directive: wrong # args.", context));
+ }
+ if(strcmp(params[1],"xt")==0){
+ bx_options.Okeyboard_type->set (BX_KBD_XT_TYPE);
+ }
+ else if(strcmp(params[1],"at")==0){
+ bx_options.Okeyboard_type->set (BX_KBD_AT_TYPE);
+ }
+ else if(strcmp(params[1],"mf")==0){
+ bx_options.Okeyboard_type->set (BX_KBD_MF_TYPE);
+ }
+ else{
+ PARSE_ERR(("%s: keyboard_type directive: wrong arg %s.", context,params[1]));
+ }
+ }
+
+ else if (!strcmp(params[0], "keyboard_mapping")
+ ||!strcmp(params[0], "keyboardmapping")) {
+ for (i=1; i<num_params; i++) {
+ if (!strncmp(params[i], "enabled=", 8)) {
+ bx_options.keyboard.OuseMapping->set (atol(&params[i][8]));
+ }
+ else if (!strncmp(params[i], "map=", 4)) {
+ bx_options.keyboard.Okeymap->set (strdup(&params[i][4]));
+ }
+ }
+ }
+ else if (!strcmp(params[0], "user_shortcut")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: user_shortcut directive: wrong # args.", context));
+ }
+ if(!strncmp(params[1], "keys=", 4)) {
+ bx_options.Ouser_shortcut->set (strdup(&params[1][5]));
+ }
+ }
+ else if (!strcmp(params[0], "config_interface")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: config_interface directive: wrong # args.", context));
+ }
+ if (!bx_options.Osel_config->set_by_name (params[1]))
+ PARSE_ERR(("%s: config_interface '%s' not available", context, params[1]));
+ }
+ else if (!strcmp(params[0], "display_library")) {
+ if (num_params != 2) {
+ PARSE_ERR(("%s: display_library directive: wrong # args.", context));
+ }
+ if (!bx_options.Osel_displaylib->set_by_name (params[1]))
+ PARSE_ERR(("%s: display library '%s' not available", context, params[1]));
+ }
+ else {
+ PARSE_ERR(( "%s: directive '%s' not understood", context, params[0]));
+ }
+ return 0;
+}
+
+static char *fdtypes[] = {
+ "none", "1_2", "1_44", "2_88", "720k", "360k", "160k", "180k", "320k"
+};
+
+
+int
+bx_write_floppy_options (FILE *fp, int drive, bx_floppy_options *opt)
+{
+ BX_ASSERT (drive==0 || drive==1);
+ if (opt->Otype->get () == BX_FLOPPY_NONE) {
+ fprintf (fp, "# no floppy%c\n", (char)'a'+drive);
+ return 0;
+ }
+ BX_ASSERT (opt->Otype->get () > BX_FLOPPY_NONE && opt->Otype->get () <= BX_FLOPPY_LAST);
+ fprintf (fp, "floppy%c: %s=\"%s\", status=%s\n",
+ (char)'a'+drive,
+ fdtypes[opt->Otype->get () - BX_FLOPPY_NONE],
+ opt->Opath->getptr (),
+ opt->Ostatus->get ()==BX_EJECTED ? "ejected" : "inserted");
+ return 0;
+}
+
+int
+bx_write_ata_options (FILE *fp, Bit8u channel, bx_ata_options *opt)
+{
+ fprintf (fp, "ata%d: enabled=%d", channel, opt->Opresent->get());
+
+ if (opt->Opresent->get()) {
+ fprintf (fp, ", ioaddr1=0x%x, ioaddr2=0x%x, irq=%d", opt->Oioaddr1->get(),
+ opt->Oioaddr2->get(), opt->Oirq->get());
+ }
+
+ fprintf (fp, "\n");
+ return 0;
+}
+
+int
+bx_write_atadevice_options (FILE *fp, Bit8u channel, Bit8u drive, bx_atadevice_options *opt)
+{
+ if (opt->Opresent->get()) {
+ fprintf (fp, "ata%d-%s: ", channel, drive==0?"master":"slave");
+
+ if (opt->Otype->get() == BX_ATA_DEVICE_DISK) {
+ fprintf (fp, "type=disk");
+
+ switch(opt->Omode->get()) {
+ case BX_ATA_MODE_FLAT:
+ fprintf (fp, ", mode=flat");
+ break;
+ case BX_ATA_MODE_CONCAT:
+ fprintf (fp, ", mode=concat");
+ break;
+ case BX_ATA_MODE_EXTDISKSIM:
+ fprintf (fp, ", mode=external");
+ break;
+ case BX_ATA_MODE_DLL_HD:
+ fprintf (fp, ", mode=dll");
+ break;
+ case BX_ATA_MODE_SPARSE:
+ fprintf (fp, ", mode=sparse");
+ break;
+ case BX_ATA_MODE_VMWARE3:
+ fprintf (fp, ", mode=vmware3");
+ break;
+// case BX_ATA_MODE_SPLIT:
+// fprintf (fp, ", mode=split");
+// break;
+ case BX_ATA_MODE_UNDOABLE:
+ fprintf (fp, ", mode=undoable");
+ break;
+ case BX_ATA_MODE_GROWING:
+ fprintf (fp, ", mode=growing");
+ break;
+ case BX_ATA_MODE_VOLATILE:
+ fprintf (fp, ", mode=volatile");
+ break;
+// case BX_ATA_MODE_Z_UNDOABLE:
+// fprintf (fp, ", mode=z-undoable");
+// break;
+// case BX_ATA_MODE_Z_VOLATILE:
+// fprintf (fp, ", mode=z-volatile");
+// break;
+ }
+
+ switch(opt->Otranslation->get()) {
+ case BX_ATA_TRANSLATION_NONE:
+ fprintf (fp, ", translation=none");
+ break;
+ case BX_ATA_TRANSLATION_LBA:
+ fprintf (fp, ", translation=lba");
+ break;
+ case BX_ATA_TRANSLATION_LARGE:
+ fprintf (fp, ", translation=large");
+ break;
+ case BX_ATA_TRANSLATION_RECHS:
+ fprintf (fp, ", translation=rechs");
+ break;
+ case BX_ATA_TRANSLATION_AUTO:
+ fprintf (fp, ", translation=auto");
+ break;
+ }
+
+ fprintf (fp, ", path=\"%s\", cylinders=%d, heads=%d, spt=%d",
+ opt->Opath->getptr(),
+ opt->Ocylinders->get(), opt->Oheads->get(), opt->Ospt->get());
+
+ if (opt->Ojournal->getptr() != NULL)
+ if ( strcmp(opt->Ojournal->getptr(), "") != 0)
+ fprintf (fp, ", journal=\"%s\"", opt->Ojournal->getptr());
+
+ }
+ else if (opt->Otype->get() == BX_ATA_DEVICE_CDROM) {
+ fprintf (fp, "type=cdrom, path=\"%s\", status=%s",
+ opt->Opath->getptr(),
+ opt->Ostatus->get ()==BX_EJECTED ? "ejected" : "inserted");
+ }
+
+ switch(opt->Obiosdetect->get()) {
+ case BX_ATA_BIOSDETECT_NONE:
+ fprintf (fp, ", biosdetect=none");
+ break;
+ case BX_ATA_BIOSDETECT_CMOS:
+ fprintf (fp, ", biosdetect=cmos");
+ break;
+ case BX_ATA_BIOSDETECT_AUTO:
+ fprintf (fp, ", biosdetect=auto");
+ break;
+ }
+ if (strlen(opt->Omodel->getptr())>0) {
+ fprintf (fp, ", model=\"%s\"", opt->Omodel->getptr());
+ }
+
+ fprintf (fp, "\n");
+ }
+ return 0;
+}
+
+int
+bx_write_parport_options (FILE *fp, bx_parport_options *opt, int n)
+{
+ fprintf (fp, "parport%d: enabled=%d", n, opt->Oenabled->get ());
+ if (opt->Oenabled->get ()) {
+ fprintf (fp, ", file=\"%s\"", opt->Ooutfile->getptr ());
+ }
+ fprintf (fp, "\n");
+ return 0;
+}
+
+int
+bx_write_serial_options (FILE *fp, bx_serial_options *opt, int n)
+{
+ fprintf (fp, "com%d: enabled=%d", n, opt->Oenabled->get ());
+ if (opt->Oenabled->get ()) {
+ fprintf (fp, ", dev=\"%s\"", opt->Odev->getptr ());
+ }
+ fprintf (fp, "\n");
+ return 0;
+}
+
+int
+bx_write_usb_options (FILE *fp, bx_usb_options *opt, int n)
+{
+ fprintf (fp, "usb%d: enabled=%d", n, opt->Oenabled->get ());
+ if (opt->Oenabled->get ()) {
+ fprintf (fp, ", ioaddr=0x%04x, irq=%d", opt->Oioaddr->get (),
+ opt->Oirq->get ());
+ }
+ fprintf (fp, "\n");
+ return 0;
+}
+
+int
+bx_write_sb16_options (FILE *fp, bx_sb16_options *opt)
+{
+ if (!opt->Opresent->get ()) {
+ fprintf (fp, "# no sb16\n");
+ return 0;
+ }
+ fprintf (fp, "sb16: midimode=%d, midi=%s, wavemode=%d, wave=%s, loglevel=%d, log=%s, dmatimer=%d\n", opt->Omidimode->get (), opt->Omidifile->getptr (), opt->Owavemode->get (), opt->Owavefile->getptr (), opt->Ologlevel->get (), opt->Ologfile->getptr (), opt->Odmatimer->get ());
+ return 0;
+}
+
+int
+bx_write_ne2k_options (FILE *fp, bx_ne2k_options *opt)
+{
+ if (!opt->Opresent->get ()) {
+ fprintf (fp, "# no ne2k\n");
+ return 0;
+ }
+ char *ptr = opt->Omacaddr->getptr ();
+ fprintf (fp, "ne2k: ioaddr=0x%x, irq=%d, mac=%02x:%02x:%02x:%02x:%02x:%02x, ethmod=%s, ethdev=%s, script=%s\n",
+ opt->Oioaddr->get (),
+ opt->Oirq->get (),
+ (unsigned int)(0xff & ptr[0]),
+ (unsigned int)(0xff & ptr[1]),
+ (unsigned int)(0xff & ptr[2]),
+ (unsigned int)(0xff & ptr[3]),
+ (unsigned int)(0xff & ptr[4]),
+ (unsigned int)(0xff & ptr[5]),
+ opt->Oethmod->get_choice(opt->Oethmod->get()),
+ opt->Oethdev->getptr (),
+ opt->Oscript->getptr ());
+ return 0;
+}
+
+int
+bx_write_loader_options (FILE *fp, bx_load32bitOSImage_t *opt)
+{
+ if (opt->OwhichOS->get () == 0) {
+ fprintf (fp, "# no loader\n");
+ return 0;
+ }
+ BX_ASSERT(opt->OwhichOS->get () == Load32bitOSLinux || opt->OwhichOS->get () == Load32bitOSNullKernel);
+ fprintf (fp, "load32bitOSImage: os=%s, path=%s, iolog=%s, initrd=%s\n",
+ (opt->OwhichOS->get () == Load32bitOSLinux) ? "linux" : "nullkernel",
+ opt->Opath->getptr (),
+ opt->Oiolog->getptr (),
+ opt->Oinitrd->getptr ());
+ return 0;
+}
+
+int
+bx_write_clock_options (FILE *fp, bx_clock_options *opt)
+{
+ fprintf (fp, "clock: ");
+
+ switch (opt->Osync->get()) {
+ case BX_CLOCK_SYNC_NONE:
+ fprintf (fp, "sync=none");
+ break;
+ case BX_CLOCK_SYNC_REALTIME:
+ fprintf (fp, "sync=realtime");
+ break;
+ case BX_CLOCK_SYNC_SLOWDOWN:
+ fprintf (fp, "sync=slowdown");
+ break;
+ case BX_CLOCK_SYNC_BOTH:
+ fprintf (fp, "sync=both");
+ break;
+ default:
+ BX_PANIC(("Unknown value for sync method"));
+ }
+
+ switch (opt->Otime0->get()) {
+ case 0: break;
+ case BX_CLOCK_TIME0_LOCAL:
+ fprintf (fp, ", time0=local");
+ break;
+ case BX_CLOCK_TIME0_UTC:
+ fprintf (fp, ", time0=utc");
+ break;
+ default:
+ fprintf (fp, ", time0=%u", opt->Otime0->get());
+ }
+
+ fprintf (fp, "\n");
+ return 0;
+}
+
+int
+bx_write_log_options (FILE *fp, bx_log_options *opt)
+{
+ fprintf (fp, "log: %s\n", opt->Ofilename->getptr ());
+ fprintf (fp, "logprefix: %s\n", opt->Oprefix->getptr ());
+ fprintf (fp, "debugger_log: %s\n", opt->Odebugger_filename->getptr ());
+ fprintf (fp, "panic: action=%s\n",
+ io->getaction(logfunctions::get_default_action (LOGLEV_PANIC)));
+ fprintf (fp, "error: action=%s\n",
+ io->getaction(logfunctions::get_default_action (LOGLEV_ERROR)));
+ fprintf (fp, "info: action=%s\n",
+ io->getaction(logfunctions::get_default_action (LOGLEV_INFO)));
+ fprintf (fp, "debug: action=%s\n",
+ io->getaction(logfunctions::get_default_action (LOGLEV_DEBUG)));
+ fprintf (fp, "pass: action=%s\n",
+ io->getaction(logfunctions::get_default_action (LOGLEV_PASS)));
+ return 0;
+}
+
+int
+bx_write_keyboard_options (FILE *fp, bx_keyboard_options *opt)
+{
+ fprintf (fp, "keyboard_mapping: enabled=%d, map=%s\n", opt->OuseMapping->get(), opt->Okeymap->getptr());
+ return 0;
+}
+
+// return values:
+// 0: written ok
+// -1: failed
+// -2: already exists, and overwrite was off
+int
+bx_write_configuration (char *rc, int overwrite)
+{
+ BX_INFO (("write configuration to %s\n", rc));
+ // check if it exists. If so, only proceed if overwrite is set.
+ FILE *fp = fopen (rc, "r");
+ if (fp != NULL) {
+ fclose (fp);
+ if (!overwrite) return -2;
+ }
+ fp = fopen (rc, "w");
+ if (fp == NULL) return -1;
+ // finally it's open and we can start writing.
+ fprintf (fp, "# configuration file generated by Bochs\n");
+ fprintf (fp, "config_interface: %s\n", bx_options.Osel_config->get_choice(bx_options.Osel_config->get()));
+ fprintf (fp, "display_library: %s\n", bx_options.Osel_displaylib->get_choice(bx_options.Osel_displaylib->get()));
+ fprintf (fp, "megs: %d\n", bx_options.memory.Osize->get ());
+ if (strlen (bx_options.rom.Opath->getptr ()) > 0)
+ fprintf (fp, "romimage: file=%s, address=0x%05x\n", bx_options.rom.Opath->getptr(), (unsigned int)bx_options.rom.Oaddress->get ());
+ else
+ fprintf (fp, "# no romimage\n");
+ if (strlen (bx_options.vgarom.Opath->getptr ()) > 0)
+ fprintf (fp, "vgaromimage: %s\n", bx_options.vgarom.Opath->getptr ());
+ else
+ fprintf (fp, "# no vgaromimage\n");
+ int bootdrive = bx_options.Obootdrive->get ();
+ fprintf (fp, "boot: %s\n", (bootdrive==BX_BOOT_FLOPPYA) ? "floppy" : (bootdrive==BX_BOOT_DISKC) ? "disk" : "cdrom");
+ // it would be nice to put this type of function as methods on
+ // the structs like bx_floppy_options::print or something.
+ bx_write_floppy_options (fp, 0, &bx_options.floppya);
+ bx_write_floppy_options (fp, 1, &bx_options.floppyb);
+ for (Bit8u channel=0; channel<BX_MAX_ATA_CHANNEL; channel++) {
+ bx_write_ata_options (fp, channel, &bx_options.ata[channel]);
+ bx_write_atadevice_options (fp, channel, 0, &bx_options.atadevice[channel][0]);
+ bx_write_atadevice_options (fp, channel, 1, &bx_options.atadevice[channel][1]);
+ }
+ if (strlen (bx_options.optrom[0].Opath->getptr ()) > 0)
+ fprintf (fp, "optromimage1: file=%s, address=0x%05x\n", bx_options.optrom[0].Opath->getptr(), (unsigned int)bx_options.optrom[0].Oaddress->get ());
+ if (strlen (bx_options.optrom[1].Opath->getptr ()) > 0)
+ fprintf (fp, "optromimage2: file=%s, address=0x%05x\n", bx_options.optrom[1].Opath->getptr(), (unsigned int)bx_options.optrom[1].Oaddress->get ());
+ if (strlen (bx_options.optrom[2].Opath->getptr ()) > 0)
+ fprintf (fp, "optromimage3: file=%s, address=0x%05x\n", bx_options.optrom[2].Opath->getptr(), (unsigned int)bx_options.optrom[2].Oaddress->get ());
+ if (strlen (bx_options.optrom[3].Opath->getptr ()) > 0)
+ fprintf (fp, "optromimage4: file=%s, address=0x%05x\n", bx_options.optrom[3].Opath->getptr(), (unsigned int)bx_options.optrom[3].Oaddress->get ());
+ bx_write_parport_options (fp, &bx_options.par[0], 1);
+ //bx_write_parport_options (fp, &bx_options.par[1], 2);
+ bx_write_serial_options (fp, &bx_options.com[0], 1);
+ //bx_write_serial_options (fp, &bx_options.com[1], 2);
+ //bx_write_serial_options (fp, &bx_options.com[2], 3);
+ //bx_write_serial_options (fp, &bx_options.com[3], 4);
+ bx_write_usb_options (fp, &bx_options.usb[0], 1);
+ bx_write_sb16_options (fp, &bx_options.sb16);
+ fprintf (fp, "floppy_bootsig_check: disabled=%d\n", bx_options.OfloppySigCheck->get ());
+ fprintf (fp, "vga_update_interval: %u\n", bx_options.Ovga_update_interval->get ());
+ fprintf (fp, "keyboard_serial_delay: %u\n", bx_options.Okeyboard_serial_delay->get ());
+ fprintf (fp, "keyboard_paste_delay: %u\n", bx_options.Okeyboard_paste_delay->get ());
+ fprintf (fp, "floppy_command_delay: %u\n", bx_options.Ofloppy_command_delay->get ());
+ fprintf (fp, "ips: %u\n", bx_options.Oips->get ());
+ fprintf (fp, "text_snapshot_check: %d\n", bx_options.Otext_snapshot_check->get ());
+ fprintf (fp, "mouse: enabled=%d\n", bx_options.Omouse_enabled->get ());
+ fprintf (fp, "private_colormap: enabled=%d\n", bx_options.Oprivate_colormap->get ());
+#if BX_WITH_AMIGAOS
+ fprintf (fp, "fullscreen: enabled=%d\n", bx_options.Ofullscreen->get ());
+ fprintf (fp, "screenmode: name=\"%s\"\n", bx_options.Oscreenmode->getptr ());
+#endif
+ fprintf (fp, "i440fxsupport: enabled=%d\n", bx_options.Oi440FXSupport->get ());
+ bx_write_clock_options (fp, &bx_options.clock);
+ bx_write_ne2k_options (fp, &bx_options.ne2k);
+ fprintf (fp, "newharddrivesupport: enabled=%d\n", bx_options.OnewHardDriveSupport->get ());
+ bx_write_loader_options (fp, &bx_options.load32bitOSImage);
+ bx_write_log_options (fp, &bx_options.log);
+ bx_write_keyboard_options (fp, &bx_options.keyboard);
+ fprintf (fp, "keyboard_type: %s\n", bx_options.Okeyboard_type->get ()==BX_KBD_XT_TYPE?"xt":
+ bx_options.Okeyboard_type->get ()==BX_KBD_AT_TYPE?"at":"mf");
+ fprintf (fp, "user_shortcut: keys=%s\n", bx_options.Ouser_shortcut->getptr ());
+ if (strlen (bx_options.cmos.Opath->getptr ()) > 0)
+ fprintf (fp, "cmosimage: %s\n", bx_options.cmos.Opath->getptr());
+ else
+ fprintf (fp, "# no cmosimage\n");
+ fclose (fp);
+ return 0;
+}
+#endif // #if BX_PROVIDE_MAIN
+
+ void
+bx_signal_handler( int signum)
+{
+ // in a multithreaded environment, a signal such as SIGINT can be sent to all
+ // threads. This function is only intended to handle signals in the
+ // simulator thread. It will simply return if called from any other thread.
+ // Otherwise the BX_PANIC() below can be called in multiple threads at
+ // once, leading to multiple threads trying to display a dialog box,
+ // leading to GUI deadlock.
+ if (!SIM->is_sim_thread ()) {
+ BX_INFO (("bx_signal_handler: ignored sig %d because it wasn't called from the simulator thread", signum));
+ return;
+ }
+#if BX_GUI_SIGHANDLER
+ if (bx_gui_sighandler) {
+ // GUI signal handler gets first priority, if the mask says it's wanted
+ if ((1<<signum) & bx_gui->get_sighandler_mask ()) {
+ bx_gui->sighandler (signum);
+ return;
+ }
+ }
+#endif
+
+#if BX_SHOW_IPS
+ extern unsigned long ips_count;
+
+ if (signum == SIGALRM ) {
+ BX_INFO(("ips = %lu", ips_count));
+ ips_count = 0;
+#ifndef __MINGW32__
+ signal(SIGALRM, bx_signal_handler);
+ alarm( 1 );
+#endif
+ return;
+ }
+#endif
+
+#if BX_GUI_SIGHANDLER
+ if (bx_gui_sighandler) {
+ if ((1<<signum) & bx_gui->get_sighandler_mask ()) {
+ bx_gui->sighandler (signum);
+ return;
+ }
+ }
+#endif
+ BX_PANIC(("SIGNAL %u caught", signum));
+}
+
diff --git a/tools/ioemu/iodev/ne2k.cc b/tools/ioemu/iodev/ne2k.cc
new file mode 100644
index 0000000000..d6e0ac4966
--- /dev/null
+++ b/tools/ioemu/iodev/ne2k.cc
@@ -0,0 +1,1608 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_NE2K_SUPPORT
+
+//Never completely fill the ne2k ring so that we never
+// hit the unclear completely full buffer condition.
+#define BX_NE2K_NEVER_FULL_RING (1)
+
+#define LOG_THIS theNE2kDevice->
+
+bx_ne2k_c *theNE2kDevice = NULL;
+
+ int
+libne2k_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theNE2kDevice = new bx_ne2k_c ();
+ bx_devices.pluginNE2kDevice = theNE2kDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theNE2kDevice, BX_PLUGIN_NE2K);
+ return(0); // Success
+}
+
+ void
+libne2k_LTX_plugin_fini(void)
+{
+}
+
+bx_ne2k_c::bx_ne2k_c(void)
+{
+ put("NE2K");
+ settype(NE2KLOG);
+ s.tx_timer_index = BX_NULL_TIMER_HANDLE;
+}
+
+
+bx_ne2k_c::~bx_ne2k_c(void)
+{
+ // nothing for now
+}
+
+//
+// reset - restore state to power-up, cancelling all i/o
+//
+void
+bx_ne2k_c::reset(unsigned type)
+{
+ BX_DEBUG (("reset"));
+ // Zero out registers and memory
+ memset( & BX_NE2K_THIS s.CR, 0, sizeof(BX_NE2K_THIS s.CR) );
+ memset( & BX_NE2K_THIS s.ISR, 0, sizeof(BX_NE2K_THIS s.ISR));
+ memset( & BX_NE2K_THIS s.IMR, 0, sizeof(BX_NE2K_THIS s.IMR));
+ memset( & BX_NE2K_THIS s.DCR, 0, sizeof(BX_NE2K_THIS s.DCR));
+ memset( & BX_NE2K_THIS s.TCR, 0, sizeof(BX_NE2K_THIS s.TCR));
+ memset( & BX_NE2K_THIS s.TSR, 0, sizeof(BX_NE2K_THIS s.TSR));
+ memset( & BX_NE2K_THIS s.RCR, 0, sizeof(BX_NE2K_THIS s.RCR));
+ memset( & BX_NE2K_THIS s.RSR, 0, sizeof(BX_NE2K_THIS s.RSR));
+ BX_NE2K_THIS s.local_dma = 0;
+ BX_NE2K_THIS s.page_start = 0;
+ BX_NE2K_THIS s.page_stop = 0;
+ BX_NE2K_THIS s.bound_ptr = 0;
+ BX_NE2K_THIS s.tx_page_start = 0;
+ BX_NE2K_THIS s.num_coll = 0;
+ BX_NE2K_THIS s.tx_bytes = 0;
+ BX_NE2K_THIS s.fifo = 0;
+ BX_NE2K_THIS s.remote_dma = 0;
+ BX_NE2K_THIS s.remote_start = 0;
+ BX_NE2K_THIS s.remote_bytes = 0;
+ BX_NE2K_THIS s.tallycnt_0 = 0;
+ BX_NE2K_THIS s.tallycnt_1 = 0;
+ BX_NE2K_THIS s.tallycnt_2 = 0;
+
+ memset( & BX_NE2K_THIS s.physaddr, 0, sizeof(BX_NE2K_THIS s.physaddr));
+ memset( & BX_NE2K_THIS s.mchash, 0, sizeof(BX_NE2K_THIS s.mchash));
+ BX_NE2K_THIS s.curr_page = 0;
+
+ BX_NE2K_THIS s.rempkt_ptr = 0;
+ BX_NE2K_THIS s.localpkt_ptr = 0;
+ BX_NE2K_THIS s.address_cnt = 0;
+
+ memset( & BX_NE2K_THIS s.mem, 0, sizeof(BX_NE2K_THIS s.mem));
+
+ // Set power-up conditions
+ BX_NE2K_THIS s.CR.stop = 1;
+ BX_NE2K_THIS s.CR.rdma_cmd = 4;
+ BX_NE2K_THIS s.ISR.reset = 1;
+ BX_NE2K_THIS s.DCR.longaddr = 1;
+ DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq);
+}
+
+//
+// read_cr/write_cr - utility routines for handling reads/writes to
+// the Command Register
+//
+Bit32u
+bx_ne2k_c::read_cr(void)
+{
+ Bit32u val =
+ (((BX_NE2K_THIS s.CR.pgsel & 0x03) << 6) |
+ ((BX_NE2K_THIS s.CR.rdma_cmd & 0x07) << 3) |
+ (BX_NE2K_THIS s.CR.tx_packet << 2) |
+ (BX_NE2K_THIS s.CR.start << 1) |
+ (BX_NE2K_THIS s.CR.stop));
+ BX_DEBUG(("read CR returns 0x%08x", val));
+ return val;
+}
+
+void
+bx_ne2k_c::write_cr(Bit32u value)
+{
+ BX_DEBUG(("wrote 0x%02x to CR", value));
+
+ // Validate remote-DMA
+ if ((value & 0x38) == 0x00) {
+ BX_DEBUG(("CR write - invalid rDMA value 0"));
+ value |= 0x20; /* dma_cmd == 4 is a safe default */
+ }
+
+ // Check for s/w reset
+ if (value & 0x01) {
+ BX_NE2K_THIS s.ISR.reset = 1;
+ BX_NE2K_THIS s.CR.stop = 1;
+ } else {
+ BX_NE2K_THIS s.CR.stop = 0;
+ }
+
+ BX_NE2K_THIS s.CR.rdma_cmd = (value & 0x38) >> 3;
+
+ // If start command issued, the RST bit in the ISR
+ // must be cleared
+ if ((value & 0x02) && !BX_NE2K_THIS s.CR.start) {
+ BX_NE2K_THIS s.ISR.reset = 0;
+ }
+
+ BX_NE2K_THIS s.CR.start = ((value & 0x02) == 0x02);
+ BX_NE2K_THIS s.CR.pgsel = (value & 0xc0) >> 6;
+
+ // Check for send-packet command
+ if (BX_NE2K_THIS s.CR.rdma_cmd == 3) {
+ // Set up DMA read from receive ring
+ BX_NE2K_THIS s.remote_start = BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.bound_ptr * 256;
+ BX_NE2K_THIS s.remote_bytes = *((Bit16u*) & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.bound_ptr * 256 + 2 - BX_NE2K_MEMSTART]);
+ BX_INFO(("Sending buffer #x%x length %d",
+ BX_NE2K_THIS s.remote_start,
+ BX_NE2K_THIS s.remote_bytes));
+ }
+
+ // Check for start-tx
+ if ((value & 0x04) && BX_NE2K_THIS s.TCR.loop_cntl) {
+ if (BX_NE2K_THIS s.TCR.loop_cntl != 1) {
+ BX_INFO(("Loop mode %d not supported.", BX_NE2K_THIS s.TCR.loop_cntl));
+ } else {
+ rx_frame (& BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 - BX_NE2K_MEMSTART],
+ BX_NE2K_THIS s.tx_bytes);
+ }
+ } else if (value & 0x04) {
+ if (BX_NE2K_THIS s.CR.stop || !BX_NE2K_THIS s.CR.start)
+ BX_PANIC(("CR write - tx start, dev in reset"));
+
+ if (BX_NE2K_THIS s.tx_bytes == 0)
+ BX_PANIC(("CR write - tx start, tx bytes == 0"));
+
+#ifdef notdef
+ // XXX debug stuff
+ printf("packet tx (%d bytes):\t", BX_NE2K_THIS s.tx_bytes);
+ for (int i = 0; i < BX_NE2K_THIS s.tx_bytes; i++) {
+ printf("%02x ", BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 -
+ BX_NE2K_MEMSTART + i]);
+ if (i && (((i+1) % 16) == 0))
+ printf("\t");
+ }
+ printf("");
+#endif
+
+ // Send the packet to the system driver
+ BX_NE2K_THIS ethdev->sendpkt(& BX_NE2K_THIS s.mem[BX_NE2K_THIS s.tx_page_start*256 - BX_NE2K_MEMSTART], BX_NE2K_THIS s.tx_bytes);
+
+ // some more debug
+ if (BX_NE2K_THIS s.tx_timer_active)
+ BX_PANIC(("CR write, tx timer still active"));
+
+ // Schedule a timer to trigger a tx-complete interrupt
+ // The number of microseconds is the bit-time / 10.
+ // The bit-time is the preamble+sfd (64 bits), the
+ // inter-frame gap (96 bits), the CRC (4 bytes), and the
+ // the number of bits in the frame (s.tx_bytes * 8).
+ //
+ bx_pc_system.activate_timer(BX_NE2K_THIS s.tx_timer_index,
+ (64 + 96 + 4*8 + BX_NE2K_THIS s.tx_bytes*8)/10,
+ 0); // not continuous
+ }
+
+ // Linux probes for an interrupt by setting up a remote-DMA read
+ // of 0 bytes with remote-DMA completion interrupts enabled.
+ // Detect this here
+ if (BX_NE2K_THIS s.CR.rdma_cmd == 0x01 &&
+ BX_NE2K_THIS s.CR.start &&
+ BX_NE2K_THIS s.remote_bytes == 0) {
+ BX_NE2K_THIS s.ISR.rdma_done = 1;
+ if (BX_NE2K_THIS s.IMR.rdma_inte) {
+ DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ }
+ }
+}
+
+//
+// chipmem_read/chipmem_write - access the 64K private RAM.
+// The ne2000 memory is accessed through the data port of
+// the asic (offset 0) after setting up a remote-DMA transfer.
+// Both byte and word accesses are allowed.
+// The first 16 bytes contains the MAC address at even locations,
+// and there is 16K of buffer memory starting at 16K
+//
+Bit32u BX_CPP_AttrRegparmN(2)
+bx_ne2k_c::chipmem_read(Bit32u address, unsigned int io_len)
+{
+ Bit32u retval = 0;
+
+ if ((io_len == 2) && (address & 0x1))
+ BX_PANIC(("unaligned chipmem word read"));
+
+ // ROM'd MAC address
+ if ((address >=0) && (address <= 31)) {
+ retval = BX_NE2K_THIS s.macaddr[address];
+ if (io_len == 2) {
+ retval |= (BX_NE2K_THIS s.macaddr[address + 1] << 8);
+ }
+ return (retval);
+ }
+
+ if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) {
+ retval = BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART];
+ if (io_len == 2) {
+ retval |= (BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] << 8);
+ }
+ return (retval);
+ }
+
+ BX_DEBUG(("out-of-bounds chipmem read, %04X", address));
+
+ return (0xff);
+}
+
+void BX_CPP_AttrRegparmN(3)
+bx_ne2k_c::chipmem_write(Bit32u address, Bit32u value, unsigned io_len)
+{
+ if ((io_len == 2) && (address & 0x1))
+ BX_PANIC(("unaligned chipmem word write"));
+
+ if ((address >= BX_NE2K_MEMSTART) && (address < BX_NE2K_MEMEND)) {
+ BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART] = value & 0xff;
+ if (io_len == 2)
+ BX_NE2K_THIS s.mem[address - BX_NE2K_MEMSTART + 1] = value >> 8;
+ } else
+ BX_DEBUG(("out-of-bounds chipmem write, %04X", address));
+}
+
+//
+// asic_read/asic_write - This is the high 16 bytes of i/o space
+// (the lower 16 bytes is for the DS8390). Only two locations
+// are used: offset 0, which is used for data transfer, and
+// offset 0xf, which is used to reset the device.
+// The data transfer port is used to as 'external' DMA to the
+// DS8390. The chip has to have the DMA registers set up, and
+// after that, insw/outsw instructions can be used to move
+// the appropriate number of bytes to/from the device.
+//
+Bit32u BX_CPP_AttrRegparmN(2)
+bx_ne2k_c::asic_read(Bit32u offset, unsigned int io_len)
+{
+ Bit32u retval = 0;
+
+ switch (offset) {
+ case 0x0: // Data register
+ //
+ // A read remote-DMA command must have been issued,
+ // and the source-address and length registers must
+ // have been initialised.
+ //
+ if (io_len > BX_NE2K_THIS s.remote_bytes)
+ {
+ BX_ERROR(("ne2K: dma read underrun iolen=%d remote_bytes=%d",io_len,BX_NE2K_THIS s.remote_bytes));
+ //return 0;
+ }
+
+ //BX_INFO(("ne2k read DMA: addr=%4x remote_bytes=%d",BX_NE2K_THIS s.remote_dma,BX_NE2K_THIS s.remote_bytes));
+ retval = chipmem_read(BX_NE2K_THIS s.remote_dma, io_len);
+ //
+ // The 8390 bumps the address and decreases the byte count
+ // by the selected word size after every access, not by
+ // the amount of data requested by the host (io_len).
+ //
+ BX_NE2K_THIS s.remote_dma += (BX_NE2K_THIS s.DCR.wdsize + 1);
+ if (BX_NE2K_THIS s.remote_dma == BX_NE2K_THIS s.page_stop << 8) {
+ BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.page_start << 8;
+ }
+ // keep s.remote_bytes from underflowing
+ if (BX_NE2K_THIS s.remote_bytes > 1)
+ BX_NE2K_THIS s.remote_bytes -= (BX_NE2K_THIS s.DCR.wdsize + 1);
+ else
+ BX_NE2K_THIS s.remote_bytes = 0;
+
+ // If all bytes have been written, signal remote-DMA complete
+ if (BX_NE2K_THIS s.remote_bytes == 0) {
+ BX_NE2K_THIS s.ISR.rdma_done = 1;
+ if (BX_NE2K_THIS s.IMR.rdma_inte) {
+ DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ }
+ }
+ break;
+
+ case 0xf: // Reset register
+ theNE2kDevice->reset(BX_RESET_SOFTWARE);
+ break;
+
+ default:
+ BX_INFO(("asic read invalid address %04x", (unsigned) offset));
+ break;
+ }
+
+ return (retval);
+}
+
+void
+bx_ne2k_c::asic_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ BX_DEBUG(("asic write addr=0x%02x, value=0x%04x", (unsigned) offset, (unsigned) value));
+ switch (offset) {
+ case 0x0: // Data register - see asic_read for a description
+
+ if ((io_len == 2) && (BX_NE2K_THIS s.DCR.wdsize == 0)) {
+ BX_PANIC(("dma write length 2 on byte mode operation"));
+ break;
+ }
+
+ if (BX_NE2K_THIS s.remote_bytes == 0)
+ BX_PANIC(("ne2K: dma write, byte count 0"));
+
+ chipmem_write(BX_NE2K_THIS s.remote_dma, value, io_len);
+ // is this right ??? asic_read uses DCR.wordsize
+ BX_NE2K_THIS s.remote_dma += io_len;
+ if (BX_NE2K_THIS s.remote_dma == BX_NE2K_THIS s.page_stop << 8) {
+ BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.page_start << 8;
+ }
+
+ BX_NE2K_THIS s.remote_bytes -= io_len;
+ if (BX_NE2K_THIS s.remote_bytes > BX_NE2K_MEMSIZ)
+ BX_NE2K_THIS s.remote_bytes = 0;
+
+ // If all bytes have been written, signal remote-DMA complete
+ if (BX_NE2K_THIS s.remote_bytes == 0) {
+ BX_NE2K_THIS s.ISR.rdma_done = 1;
+ if (BX_NE2K_THIS s.IMR.rdma_inte) {
+ DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ }
+ }
+ break;
+
+ case 0xf: // Reset register
+ theNE2kDevice->reset(BX_RESET_SOFTWARE);
+ break;
+
+ default: // this is invalid, but happens under win95 device detection
+ BX_INFO(("asic write invalid address %04x, ignoring", (unsigned) offset));
+ break ;
+ }
+}
+
+//
+// page0_read/page0_write - These routines handle reads/writes to
+// the 'zeroth' page of the DS8390 register file
+//
+Bit32u
+bx_ne2k_c::page0_read(Bit32u offset, unsigned int io_len)
+{
+ BX_DEBUG(("page 0 read from port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len));
+ if (io_len > 1) {
+ BX_ERROR(("bad length! page 0 read from port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len)); /* encountered with win98 hardware probe */
+ return 0;
+ }
+
+
+ switch (offset) {
+ case 0x0: // CR
+ return (read_cr());
+ break;
+
+ case 0x1: // CLDA0
+ return (BX_NE2K_THIS s.local_dma & 0xff);
+ break;
+
+ case 0x2: // CLDA1
+ return (BX_NE2K_THIS s.local_dma >> 8);
+ break;
+
+ case 0x3: // BNRY
+ return (BX_NE2K_THIS s.bound_ptr);
+ break;
+
+ case 0x4: // TSR
+ return ((BX_NE2K_THIS s.TSR.ow_coll << 7) |
+ (BX_NE2K_THIS s.TSR.cd_hbeat << 6) |
+ (BX_NE2K_THIS s.TSR.fifo_ur << 5) |
+ (BX_NE2K_THIS s.TSR.no_carrier << 4) |
+ (BX_NE2K_THIS s.TSR.aborted << 3) |
+ (BX_NE2K_THIS s.TSR.collided << 2) |
+ (BX_NE2K_THIS s.TSR.tx_ok));
+ break;
+
+ case 0x5: // NCR
+ return (BX_NE2K_THIS s.num_coll);
+ break;
+
+ case 0x6: // FIFO
+ // reading FIFO is only valid in loopback mode
+ BX_ERROR(("reading FIFO not supported yet"));
+ return (BX_NE2K_THIS s.fifo);
+ break;
+
+ case 0x7: // ISR
+ return ((BX_NE2K_THIS s.ISR.reset << 7) |
+ (BX_NE2K_THIS s.ISR.rdma_done << 6) |
+ (BX_NE2K_THIS s.ISR.cnt_oflow << 5) |
+ (BX_NE2K_THIS s.ISR.overwrite << 4) |
+ (BX_NE2K_THIS s.ISR.tx_err << 3) |
+ (BX_NE2K_THIS s.ISR.rx_err << 2) |
+ (BX_NE2K_THIS s.ISR.pkt_tx << 1) |
+ (BX_NE2K_THIS s.ISR.pkt_rx));
+ break;
+
+ case 0x8: // CRDA0
+ return (BX_NE2K_THIS s.remote_dma & 0xff);
+ break;
+
+ case 0x9: // CRDA1
+ return (BX_NE2K_THIS s.remote_dma >> 8);
+ break;
+
+ case 0xa: // reserved
+ BX_INFO(("reserved read - page 0, 0xa"));
+ return (0xff);
+ break;
+
+ case 0xb: // reserved
+ BX_INFO(("reserved read - page 0, 0xb"));
+ return (0xff);
+ break;
+
+ case 0xc: // RSR
+ return ((BX_NE2K_THIS s.RSR.deferred << 7) |
+ (BX_NE2K_THIS s.RSR.rx_disabled << 6) |
+ (BX_NE2K_THIS s.RSR.rx_mbit << 5) |
+ (BX_NE2K_THIS s.RSR.rx_missed << 4) |
+ (BX_NE2K_THIS s.RSR.fifo_or << 3) |
+ (BX_NE2K_THIS s.RSR.bad_falign << 2) |
+ (BX_NE2K_THIS s.RSR.bad_crc << 1) |
+ (BX_NE2K_THIS s.RSR.rx_ok));
+ break;
+
+ case 0xd: // CNTR0
+ return (BX_NE2K_THIS s.tallycnt_0);
+ break;
+
+ case 0xe: // CNTR1
+ return (BX_NE2K_THIS s.tallycnt_1);
+ break;
+
+ case 0xf: // CNTR2
+ return (BX_NE2K_THIS s.tallycnt_2);
+ break;
+
+ default:
+ BX_PANIC(("page 0 offset %04x out of range", (unsigned) offset));
+ }
+
+ return(0);
+}
+
+void
+bx_ne2k_c::page0_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ BX_DEBUG(("page 0 write to port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len));
+
+ // It appears to be a common practice to use outw on page0 regs...
+
+ // break up outw into two outb's
+ if (io_len == 2) {
+ page0_write(offset, (value & 0xff), 1);
+ page0_write(offset + 1, ((value >> 8) & 0xff), 1);
+ return;
+ }
+
+ switch (offset) {
+ case 0x0: // CR
+ write_cr(value);
+ break;
+
+ case 0x1: // PSTART
+ BX_NE2K_THIS s.page_start = value;
+ break;
+
+ case 0x2: // PSTOP
+ // BX_INFO(("Writing to PSTOP: %02x", value));
+ BX_NE2K_THIS s.page_stop = value;
+ break;
+
+ case 0x3: // BNRY
+ BX_NE2K_THIS s.bound_ptr = value;
+ break;
+
+ case 0x4: // TPSR
+ BX_NE2K_THIS s.tx_page_start = value;
+ break;
+
+ case 0x5: // TBCR0
+ // Clear out low byte and re-insert
+ BX_NE2K_THIS s.tx_bytes &= 0xff00;
+ BX_NE2K_THIS s.tx_bytes |= (value & 0xff);
+ break;
+
+ case 0x6: // TBCR1
+ // Clear out high byte and re-insert
+ BX_NE2K_THIS s.tx_bytes &= 0x00ff;
+ BX_NE2K_THIS s.tx_bytes |= ((value & 0xff) << 8);
+ break;
+
+ case 0x7: // ISR
+ value &= 0x7f; // clear RST bit - status-only bit
+ // All other values are cleared iff the ISR bit is 1
+ BX_NE2K_THIS s.ISR.pkt_rx &= ~((bx_bool)((value & 0x01) == 0x01));
+ BX_NE2K_THIS s.ISR.pkt_tx &= ~((bx_bool)((value & 0x02) == 0x02));
+ BX_NE2K_THIS s.ISR.rx_err &= ~((bx_bool)((value & 0x04) == 0x04));
+ BX_NE2K_THIS s.ISR.tx_err &= ~((bx_bool)((value & 0x08) == 0x08));
+ BX_NE2K_THIS s.ISR.overwrite &= ~((bx_bool)((value & 0x10) == 0x10));
+ BX_NE2K_THIS s.ISR.cnt_oflow &= ~((bx_bool)((value & 0x20) == 0x20));
+ BX_NE2K_THIS s.ISR.rdma_done &= ~((bx_bool)((value & 0x40) == 0x40));
+ value = ((BX_NE2K_THIS s.ISR.rdma_done << 6) |
+ (BX_NE2K_THIS s.ISR.cnt_oflow << 5) |
+ (BX_NE2K_THIS s.ISR.overwrite << 4) |
+ (BX_NE2K_THIS s.ISR.tx_err << 3) |
+ (BX_NE2K_THIS s.ISR.rx_err << 2) |
+ (BX_NE2K_THIS s.ISR.pkt_tx << 1) |
+ (BX_NE2K_THIS s.ISR.pkt_rx));
+ value &= ((BX_NE2K_THIS s.IMR.rdma_inte << 6) |
+ (BX_NE2K_THIS s.IMR.cofl_inte << 5) |
+ (BX_NE2K_THIS s.IMR.overw_inte << 4) |
+ (BX_NE2K_THIS s.IMR.txerr_inte << 3) |
+ (BX_NE2K_THIS s.IMR.rxerr_inte << 2) |
+ (BX_NE2K_THIS s.IMR.tx_inte << 1) |
+ (BX_NE2K_THIS s.IMR.rx_inte));
+ if (value == 0)
+ DEV_pic_lower_irq(BX_NE2K_THIS s.base_irq);
+ break;
+
+ case 0x8: // RSAR0
+ // Clear out low byte and re-insert
+ BX_NE2K_THIS s.remote_start &= 0xff00;
+ BX_NE2K_THIS s.remote_start |= (value & 0xff);
+ BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.remote_start;
+ break;
+
+ case 0x9: // RSAR1
+ // Clear out high byte and re-insert
+ BX_NE2K_THIS s.remote_start &= 0x00ff;
+ BX_NE2K_THIS s.remote_start |= ((value & 0xff) << 8);
+ BX_NE2K_THIS s.remote_dma = BX_NE2K_THIS s.remote_start;
+ break;
+
+ case 0xa: // RBCR0
+ // Clear out low byte and re-insert
+ BX_NE2K_THIS s.remote_bytes &= 0xff00;
+ BX_NE2K_THIS s.remote_bytes |= (value & 0xff);
+ break;
+
+ case 0xb: // RBCR1
+ // Clear out high byte and re-insert
+ BX_NE2K_THIS s.remote_bytes &= 0x00ff;
+ BX_NE2K_THIS s.remote_bytes |= ((value & 0xff) << 8);
+ break;
+
+ case 0xc: // RCR
+ // Check if the reserved bits are set
+ if (value & 0xc0)
+ BX_INFO(("RCR write, reserved bits set"));
+
+ // Set all other bit-fields
+ BX_NE2K_THIS s.RCR.errors_ok = ((value & 0x01) == 0x01);
+ BX_NE2K_THIS s.RCR.runts_ok = ((value & 0x02) == 0x02);
+ BX_NE2K_THIS s.RCR.broadcast = ((value & 0x04) == 0x04);
+ BX_NE2K_THIS s.RCR.multicast = ((value & 0x08) == 0x08);
+ BX_NE2K_THIS s.RCR.promisc = ((value & 0x10) == 0x10);
+ BX_NE2K_THIS s.RCR.monitor = ((value & 0x20) == 0x20);
+
+ // Monitor bit is a little suspicious...
+ if (value & 0x20)
+ BX_INFO(("RCR write, monitor bit set!"));
+ break;
+
+ case 0xd: // TCR
+ // Check reserved bits
+ if (value & 0xe0)
+ BX_ERROR(("TCR write, reserved bits set"));
+
+ // Test loop mode (not supported)
+ if (value & 0x06) {
+ BX_NE2K_THIS s.TCR.loop_cntl = (value & 0x6) >> 1;
+ BX_INFO(("TCR write, loop mode %d not supported", BX_NE2K_THIS s.TCR.loop_cntl));
+ } else {
+ BX_NE2K_THIS s.TCR.loop_cntl = 0;
+ }
+
+ // Inhibit-CRC not supported.
+ if (value & 0x01)
+ BX_PANIC(("TCR write, inhibit-CRC not supported"));
+
+ // Auto-transmit disable very suspicious
+ if (value & 0x08)
+ BX_PANIC(("TCR write, auto transmit disable not supported"));
+
+ // Allow collision-offset to be set, although not used
+ BX_NE2K_THIS s.TCR.coll_prio = ((value & 0x08) == 0x08);
+ break;
+
+ case 0xe: // DCR
+ // the loopback mode is not suppported yet
+ if (!(value & 0x08)) {
+ BX_ERROR(("DCR write, loopback mode selected"));
+ }
+ // It is questionable to set longaddr and auto_rx, since they
+ // aren't supported on the ne2000. Print a warning and continue
+ if (value & 0x04)
+ BX_INFO(("DCR write - LAS set ???"));
+ if (value & 0x10)
+ BX_INFO(("DCR write - AR set ???"));
+
+ // Set other values.
+ BX_NE2K_THIS s.DCR.wdsize = ((value & 0x01) == 0x01);
+ BX_NE2K_THIS s.DCR.endian = ((value & 0x02) == 0x02);
+ BX_NE2K_THIS s.DCR.longaddr = ((value & 0x04) == 0x04); // illegal ?
+ BX_NE2K_THIS s.DCR.loop = ((value & 0x08) == 0x08);
+ BX_NE2K_THIS s.DCR.auto_rx = ((value & 0x10) == 0x10); // also illegal ?
+ BX_NE2K_THIS s.DCR.fifo_size = (value & 0x50) >> 5;
+ break;
+
+ case 0xf: // IMR
+ // Check for reserved bit
+ if (value & 0x80)
+ BX_PANIC(("IMR write, reserved bit set"));
+
+ // Set other values
+ BX_NE2K_THIS s.IMR.rx_inte = ((value & 0x01) == 0x01);
+ BX_NE2K_THIS s.IMR.tx_inte = ((value & 0x02) == 0x02);
+ BX_NE2K_THIS s.IMR.rxerr_inte = ((value & 0x04) == 0x04);
+ BX_NE2K_THIS s.IMR.txerr_inte = ((value & 0x08) == 0x08);
+ BX_NE2K_THIS s.IMR.overw_inte = ((value & 0x10) == 0x10);
+ BX_NE2K_THIS s.IMR.cofl_inte = ((value & 0x20) == 0x20);
+ BX_NE2K_THIS s.IMR.rdma_inte = ((value & 0x40) == 0x40);
+ break;
+
+ default:
+ BX_PANIC(("page 0 write, bad offset %0x", offset));
+ }
+}
+
+
+//
+// page1_read/page1_write - These routines handle reads/writes to
+// the first page of the DS8390 register file
+//
+Bit32u
+bx_ne2k_c::page1_read(Bit32u offset, unsigned int io_len)
+{
+ BX_DEBUG(("page 1 read from port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len));
+ if (io_len > 1)
+ BX_PANIC(("bad length! page 1 read from port %04x, len=%u", (unsigned) offset,
+ (unsigned) io_len));
+
+ switch (offset) {
+ case 0x0: // CR
+ return (read_cr());
+ break;
+
+ case 0x1: // PAR0-5
+ case 0x2:
+ case 0x3:
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ return (BX_NE2K_THIS s.physaddr[offset - 1]);
+ break;
+
+ case 0x7: // CURR
+ BX_DEBUG(("returning current page: %02x", (BX_NE2K_THIS s.curr_page)));
+ return (BX_NE2K_THIS s.curr_page);
+
+ case 0x8: // MAR0-7
+ case 0x9:
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ case 0xf:
+ return (BX_NE2K_THIS s.mchash[offset - 8]);
+ break;
+
+ default:
+ BX_PANIC(("page 1 r offset %04x out of range", (unsigned) offset));
+ }
+
+ return (0);
+}
+
+void
+bx_ne2k_c::page1_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ BX_DEBUG(("page 1 w offset %04x", (unsigned) offset));
+ switch (offset) {
+ case 0x0: // CR
+ write_cr(value);
+ break;
+
+ case 0x1: // PAR0-5
+ case 0x2:
+ case 0x3:
+ case 0x4:
+ case 0x5:
+ case 0x6:
+ BX_NE2K_THIS s.physaddr[offset - 1] = value;
+ break;
+
+ case 0x7: // CURR
+ BX_NE2K_THIS s.curr_page = value;
+ break;
+
+ case 0x8: // MAR0-7
+ case 0x9:
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ case 0xf:
+ BX_NE2K_THIS s.mchash[offset - 8] = value;
+ break;
+
+ default:
+ BX_PANIC(("page 1 w offset %04x out of range", (unsigned) offset));
+ }
+}
+
+
+//
+// page2_read/page2_write - These routines handle reads/writes to
+// the second page of the DS8390 register file
+//
+Bit32u
+bx_ne2k_c::page2_read(Bit32u offset, unsigned int io_len)
+{
+ BX_DEBUG(("page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len));
+
+ if (io_len > 1)
+ BX_PANIC(("bad length! page 2 read from port %04x, len=%u", (unsigned) offset, (unsigned) io_len));
+
+ switch (offset) {
+ case 0x0: // CR
+ return (read_cr());
+ break;
+
+ case 0x1: // PSTART
+ return (BX_NE2K_THIS s.page_start);
+ break;
+
+ case 0x2: // PSTOP
+ return (BX_NE2K_THIS s.page_stop);
+ break;
+
+ case 0x3: // Remote Next-packet pointer
+ return (BX_NE2K_THIS s.rempkt_ptr);
+ break;
+
+ case 0x4: // TPSR
+ return (BX_NE2K_THIS s.tx_page_start);
+ break;
+
+ case 0x5: // Local Next-packet pointer
+ return (BX_NE2K_THIS s.localpkt_ptr);
+ break;
+
+ case 0x6: // Address counter (upper)
+ return (BX_NE2K_THIS s.address_cnt >> 8);
+ break;
+
+ case 0x7: // Address counter (lower)
+ return (BX_NE2K_THIS s.address_cnt & 0xff);
+ break;
+
+ case 0x8: // Reserved
+ case 0x9:
+ case 0xa:
+ case 0xb:
+ BX_ERROR(("reserved read - page 2, 0x%02x", (unsigned) offset));
+ return (0xff);
+ break;
+
+ case 0xc: // RCR
+ return ((BX_NE2K_THIS s.RCR.monitor << 5) |
+ (BX_NE2K_THIS s.RCR.promisc << 4) |
+ (BX_NE2K_THIS s.RCR.multicast << 3) |
+ (BX_NE2K_THIS s.RCR.broadcast << 2) |
+ (BX_NE2K_THIS s.RCR.runts_ok << 1) |
+ (BX_NE2K_THIS s.RCR.errors_ok));
+ break;
+
+ case 0xd: // TCR
+ return ((BX_NE2K_THIS s.TCR.coll_prio << 4) |
+ (BX_NE2K_THIS s.TCR.ext_stoptx << 3) |
+ ((BX_NE2K_THIS s.TCR.loop_cntl & 0x3) << 1) |
+ (BX_NE2K_THIS s.TCR.crc_disable));
+ break;
+
+ case 0xe: // DCR
+ return (((BX_NE2K_THIS s.DCR.fifo_size & 0x3) << 5) |
+ (BX_NE2K_THIS s.DCR.auto_rx << 4) |
+ (BX_NE2K_THIS s.DCR.loop << 3) |
+ (BX_NE2K_THIS s.DCR.longaddr << 2) |
+ (BX_NE2K_THIS s.DCR.endian << 1) |
+ (BX_NE2K_THIS s.DCR.wdsize));
+ break;
+
+ case 0xf: // IMR
+ return ((BX_NE2K_THIS s.IMR.rdma_inte << 6) |
+ (BX_NE2K_THIS s.IMR.cofl_inte << 5) |
+ (BX_NE2K_THIS s.IMR.overw_inte << 4) |
+ (BX_NE2K_THIS s.IMR.txerr_inte << 3) |
+ (BX_NE2K_THIS s.IMR.rxerr_inte << 2) |
+ (BX_NE2K_THIS s.IMR.tx_inte << 1) |
+ (BX_NE2K_THIS s.IMR.rx_inte));
+ break;
+
+ default:
+ BX_PANIC(("page 2 offset %04x out of range", (unsigned) offset));
+ }
+
+ return (0);
+};
+
+void
+bx_ne2k_c::page2_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ // Maybe all writes here should be BX_PANIC()'d, since they
+ // affect internal operation, but let them through for now
+ // and print a warning.
+ if (offset != 0)
+ BX_ERROR(("page 2 write ?"));
+
+ switch (offset) {
+ case 0x0: // CR
+ write_cr(value);
+ break;
+
+ case 0x1: // CLDA0
+ // Clear out low byte and re-insert
+ BX_NE2K_THIS s.local_dma &= 0xff00;
+ BX_NE2K_THIS s.local_dma |= (value & 0xff);
+ break;
+
+ case 0x2: // CLDA1
+ // Clear out high byte and re-insert
+ BX_NE2K_THIS s.local_dma &= 0x00ff;
+ BX_NE2K_THIS s.local_dma |= ((value & 0xff) << 8);
+ break;
+
+ case 0x3: // Remote Next-pkt pointer
+ BX_NE2K_THIS s.rempkt_ptr = value;
+ break;
+
+ case 0x4:
+ BX_PANIC(("page 2 write to reserved offset 4"));
+ break;
+
+ case 0x5: // Local Next-packet pointer
+ BX_NE2K_THIS s.localpkt_ptr = value;
+ break;
+
+ case 0x6: // Address counter (upper)
+ // Clear out high byte and re-insert
+ BX_NE2K_THIS s.address_cnt &= 0x00ff;
+ BX_NE2K_THIS s.address_cnt |= ((value & 0xff) << 8);
+ break;
+
+ case 0x7: // Address counter (lower)
+ // Clear out low byte and re-insert
+ BX_NE2K_THIS s.address_cnt &= 0xff00;
+ BX_NE2K_THIS s.address_cnt |= (value & 0xff);
+ break;
+
+ case 0x8:
+ case 0x9:
+ case 0xa:
+ case 0xb:
+ case 0xc:
+ case 0xd:
+ case 0xe:
+ case 0xf:
+ BX_PANIC(("page 2 write to reserved offset %0x", offset));
+ break;
+
+ default:
+ BX_PANIC(("page 2 write, illegal offset %0x", offset));
+ break;
+ }
+}
+
+//
+// page3_read/page3_write - writes to this page are illegal
+//
+Bit32u
+bx_ne2k_c::page3_read(Bit32u offset, unsigned int io_len)
+{
+ BX_PANIC(("page 3 read attempted"));
+ return (0);
+}
+
+void
+bx_ne2k_c::page3_write(Bit32u offset, Bit32u value, unsigned io_len)
+{
+ BX_PANIC(("page 3 write attempted"));
+}
+
+//
+// tx_timer_handler/tx_timer
+//
+void
+bx_ne2k_c::tx_timer_handler(void *this_ptr)
+{
+ bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr;
+
+ class_ptr->tx_timer();
+}
+
+void
+bx_ne2k_c::tx_timer(void)
+{
+ BX_DEBUG(("tx_timer"));
+ BX_NE2K_THIS s.TSR.tx_ok = 1;
+ // Generate an interrupt if not masked and not one in progress
+ if (BX_NE2K_THIS s.IMR.tx_inte && !BX_NE2K_THIS s.ISR.pkt_tx) {
+ BX_NE2K_THIS s.ISR.pkt_tx = 1;
+ DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ }
+ BX_NE2K_THIS s.tx_timer_active = 0;
+}
+
+
+//
+// read_handler/read - i/o 'catcher' function called from BOCHS
+// mainline when the CPU attempts a read in the i/o space registered
+// by this ne2000 instance
+//
+Bit32u
+bx_ne2k_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_NE2K_SMF
+ bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+Bit32u
+bx_ne2k_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_NE2K_SMF
+ BX_DEBUG(("read addr %x, len %d", address, io_len));
+ Bit32u retval = 0;
+ int offset = address - BX_NE2K_THIS s.base_address;
+
+ if (offset >= 0x10) {
+ retval = asic_read(offset - 0x10, io_len);
+ } else {
+ switch (BX_NE2K_THIS s.CR.pgsel) {
+ case 0x00:
+ retval = page0_read(offset, io_len);
+ break;
+
+ case 0x01:
+ retval = page1_read(offset, io_len);
+ break;
+
+ case 0x02:
+ retval = page2_read(offset, io_len);
+ break;
+
+ case 0x03:
+ retval = page3_read(offset, io_len);
+ break;
+
+ default:
+ BX_PANIC(("ne2K: unknown value of pgsel in read - %d",
+ BX_NE2K_THIS s.CR.pgsel));
+ }
+ }
+
+ return (retval);
+}
+
+//
+// write_handler/write - i/o 'catcher' function called from BOCHS
+// mainline when the CPU attempts a write in the i/o space registered
+// by this ne2000 instance
+//
+void
+bx_ne2k_c::write_handler(void *this_ptr, Bit32u address, Bit32u value,
+ unsigned io_len)
+{
+#if !BX_USE_NE2K_SMF
+ bx_ne2k_c *class_ptr = (bx_ne2k_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+void
+bx_ne2k_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_NE2K_SMF
+ BX_DEBUG(("write with length %d", io_len));
+ int offset = address - BX_NE2K_THIS s.base_address;
+
+ //
+ // The high 16 bytes of i/o space are for the ne2000 asic -
+ // the low 16 bytes are for the DS8390, with the current
+ // page being selected by the PS0,PS1 registers in the
+ // command register
+ //
+ if (offset >= 0x10) {
+ asic_write(offset - 0x10, value, io_len);
+ } else {
+ switch (BX_NE2K_THIS s.CR.pgsel) {
+ case 0x00:
+ page0_write(offset, value, io_len);
+ break;
+
+ case 0x01:
+ page1_write(offset, value, io_len);
+ break;
+
+ case 0x02:
+ page2_write(offset, value, io_len);
+ break;
+
+ case 0x03:
+ page3_write(offset, value, io_len);
+ break;
+
+ default:
+ BX_PANIC(("ne2K: unknown value of pgsel in write - %d",
+ BX_NE2K_THIS s.CR.pgsel));
+ }
+ }
+}
+
+
+/*
+ * mcast_index() - return the 6-bit index into the multicast
+ * table. Stolen unashamedly from FreeBSD's if_ed.c
+ */
+unsigned
+bx_ne2k_c::mcast_index(const void *dst)
+{
+#define POLYNOMIAL 0x04c11db6
+ unsigned long crc = 0xffffffffL;
+ int carry, i, j;
+ unsigned char b;
+ unsigned char *ep = (unsigned char *) dst;
+
+ for (i = 6; --i >= 0;) {
+ b = *ep++;
+ for (j = 8; --j >= 0;) {
+ carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
+ crc <<= 1;
+ b >>= 1;
+ if (carry)
+ crc = ((crc ^ POLYNOMIAL) | carry);
+ }
+ }
+ return (crc >> 26);
+#undef POLYNOMIAL
+}
+
+/*
+ * Callback from the eth system driver when a frame has arrived
+ */
+void
+bx_ne2k_c::rx_handler(void *arg, const void *buf, unsigned len)
+{
+ // BX_DEBUG(("rx_handler with length %d", len));
+ bx_ne2k_c *class_ptr = (bx_ne2k_c *) arg;
+
+ class_ptr->rx_frame(buf, len);
+}
+
+/*
+ * rx_frame() - called by the platform-specific code when an
+ * ethernet frame has been received. The destination address
+ * is tested to see if it should be accepted, and if the
+ * rx ring has enough room, it is copied into it and
+ * the receive process is updated
+ */
+void
+bx_ne2k_c::rx_frame(const void *buf, unsigned io_len)
+{
+ unsigned pages;
+ unsigned avail;
+ unsigned idx;
+ int wrapped;
+ int nextpage;
+ unsigned char pkthdr[4];
+ unsigned char *pktbuf = (unsigned char *) buf;
+ unsigned char *startptr;
+ static unsigned char bcast_addr[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+
+ BX_DEBUG(("rx_frame with length %d", io_len));
+
+
+ if ((BX_NE2K_THIS s.CR.stop != 0) ||
+ (BX_NE2K_THIS s.page_start == 0) ||
+ ((BX_NE2K_THIS s.DCR.loop == 0) &&
+ (BX_NE2K_THIS s.TCR.loop_cntl != 0))) {
+
+ return;
+ }
+
+ // Add the pkt header + CRC to the length, and work
+ // out how many 256-byte pages the frame would occupy
+ pages = (io_len + 4 + 4 + 255)/256;
+
+ if (BX_NE2K_THIS s.curr_page < BX_NE2K_THIS s.bound_ptr) {
+ avail = BX_NE2K_THIS s.bound_ptr - BX_NE2K_THIS s.curr_page;
+ } else {
+ avail = (BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.page_start) -
+ (BX_NE2K_THIS s.curr_page - BX_NE2K_THIS s.bound_ptr);
+ wrapped = 1;
+ }
+
+ // Avoid getting into a buffer overflow condition by not attempting
+ // to do partial receives. The emulation to handle this condition
+ // seems particularly painful.
+ if ((avail < pages)
+#if BX_NE2K_NEVER_FULL_RING
+ || (avail == pages)
+#endif
+ ) {
+ return;
+ }
+
+ if ((io_len < 60) && !BX_NE2K_THIS s.RCR.runts_ok) {
+ BX_DEBUG(("rejected small packet, length %d", io_len));
+ return;
+ }
+
+ // Do address filtering if not in promiscuous mode
+ if (! BX_NE2K_THIS s.RCR.promisc) {
+ if (!memcmp(buf, bcast_addr, 6)) {
+ if (!BX_NE2K_THIS s.RCR.broadcast) {
+ return;
+ }
+ } else if (pktbuf[0] & 0x01) {
+ if (! BX_NE2K_THIS s.RCR.multicast) {
+ return;
+ }
+ idx = mcast_index(buf);
+ if (!(BX_NE2K_THIS s.mchash[idx >> 3] & (1 << (idx & 0x7)))) {
+ return;
+ }
+ } else if (0 != memcmp(buf, BX_NE2K_THIS s.physaddr, 6)) {
+ return;
+ }
+ } else {
+ BX_DEBUG(("rx_frame promiscuous receive"));
+ }
+
+// BX_INFO(("rx_frame %d to %x:%x:%x:%x:%x:%x from %x:%x:%x:%x:%x:%x",
+// io_len,
+// pktbuf[0], pktbuf[1], pktbuf[2], pktbuf[3], pktbuf[4], pktbuf[5],
+// pktbuf[6], pktbuf[7], pktbuf[8], pktbuf[9], pktbuf[10], pktbuf[11]));
+
+ nextpage = BX_NE2K_THIS s.curr_page + pages;
+ if (nextpage >= BX_NE2K_THIS s.page_stop) {
+ nextpage -= BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.page_start;
+ }
+
+ // Setup packet header
+ pkthdr[0] = 0; // rx status - old behavior
+ pkthdr[0] = 1; // Probably better to set it all the time
+ // rather than set it to 0, which is clearly wrong.
+ if (pktbuf[0] & 0x01) {
+ pkthdr[0] |= 0x20; // rx status += multicast packet
+ }
+ pkthdr[1] = nextpage; // ptr to next packet
+ pkthdr[2] = (io_len + 4) & 0xff; // length-low
+ pkthdr[3] = (io_len + 4) >> 8; // length-hi
+
+ // copy into buffer, update curpage, and signal interrupt if config'd
+ startptr = & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.curr_page * 256 -
+ BX_NE2K_MEMSTART];
+ if ((nextpage > BX_NE2K_THIS s.curr_page) ||
+ ((BX_NE2K_THIS s.curr_page + pages) == BX_NE2K_THIS s.page_stop)) {
+ memcpy(startptr, pkthdr, 4);
+ memcpy(startptr + 4, buf, io_len);
+ BX_NE2K_THIS s.curr_page = nextpage;
+ } else {
+ int endbytes = (BX_NE2K_THIS s.page_stop - BX_NE2K_THIS s.curr_page)
+ * 256;
+ memcpy(startptr, pkthdr, 4);
+ memcpy(startptr + 4, buf, endbytes - 4);
+ startptr = & BX_NE2K_THIS s.mem[BX_NE2K_THIS s.page_start * 256 -
+ BX_NE2K_MEMSTART];
+ memcpy(startptr, (void *)(pktbuf + endbytes - 4),
+ io_len - endbytes + 8);
+ BX_NE2K_THIS s.curr_page = nextpage;
+ }
+
+ BX_NE2K_THIS s.RSR.rx_ok = 1;
+ if (pktbuf[0] & 0x80) {
+ BX_NE2K_THIS s.RSR.rx_mbit = 1;
+ }
+
+ BX_NE2K_THIS s.ISR.pkt_rx = 1;
+
+ if (BX_NE2K_THIS s.IMR.rx_inte) {
+ DEV_pic_raise_irq(BX_NE2K_THIS s.base_irq);
+ }
+
+}
+
+void
+bx_ne2k_c::init(void)
+{
+ BX_DEBUG(("Init $Id: ne2k.cc,v 1.56.2.1 2004/02/02 22:37:22 cbothamy Exp $"));
+
+ // Read in values from config file
+ BX_NE2K_THIS s.base_address = bx_options.ne2k.Oioaddr->get ();
+ BX_NE2K_THIS s.base_irq = bx_options.ne2k.Oirq->get ();
+ memcpy(BX_NE2K_THIS s.physaddr, bx_options.ne2k.Omacaddr->getptr (), 6);
+
+ if (BX_NE2K_THIS s.tx_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_NE2K_THIS s.tx_timer_index =
+ bx_pc_system.register_timer(this, tx_timer_handler, 0,
+ 0,0, "ne2k"); // one-shot, inactive
+ }
+ // Register the IRQ and i/o port addresses
+ DEV_register_irq(BX_NE2K_THIS s.base_irq, "NE2000 ethernet NIC");
+
+ for (unsigned addr = BX_NE2K_THIS s.base_address;
+ addr <= BX_NE2K_THIS s.base_address + 0x20;
+ addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "ne2000 NIC", 3);
+ DEV_register_iowrite_handler(this, write_handler, addr, "ne2000 NIC", 3);
+ }
+ BX_INFO(("port 0x%x/32 irq %d mac %02x:%02x:%02x:%02x:%02x:%02x",
+ BX_NE2K_THIS s.base_address,
+ BX_NE2K_THIS s.base_irq,
+ BX_NE2K_THIS s.physaddr[0],
+ BX_NE2K_THIS s.physaddr[1],
+ BX_NE2K_THIS s.physaddr[2],
+ BX_NE2K_THIS s.physaddr[3],
+ BX_NE2K_THIS s.physaddr[4],
+ BX_NE2K_THIS s.physaddr[5]));
+
+ // Initialise the mac address area by doubling the physical address
+ BX_NE2K_THIS s.macaddr[0] = BX_NE2K_THIS s.physaddr[0];
+ BX_NE2K_THIS s.macaddr[1] = BX_NE2K_THIS s.physaddr[0];
+ BX_NE2K_THIS s.macaddr[2] = BX_NE2K_THIS s.physaddr[1];
+ BX_NE2K_THIS s.macaddr[3] = BX_NE2K_THIS s.physaddr[1];
+ BX_NE2K_THIS s.macaddr[4] = BX_NE2K_THIS s.physaddr[2];
+ BX_NE2K_THIS s.macaddr[5] = BX_NE2K_THIS s.physaddr[2];
+ BX_NE2K_THIS s.macaddr[6] = BX_NE2K_THIS s.physaddr[3];
+ BX_NE2K_THIS s.macaddr[7] = BX_NE2K_THIS s.physaddr[3];
+ BX_NE2K_THIS s.macaddr[8] = BX_NE2K_THIS s.physaddr[4];
+ BX_NE2K_THIS s.macaddr[9] = BX_NE2K_THIS s.physaddr[4];
+ BX_NE2K_THIS s.macaddr[10] = BX_NE2K_THIS s.physaddr[5];
+ BX_NE2K_THIS s.macaddr[11] = BX_NE2K_THIS s.physaddr[5];
+
+ // ne2k signature
+ for (int i = 12; i < 32; i++)
+ BX_NE2K_THIS s.macaddr[i] = 0x57;
+
+ // Attach to the simulated ethernet dev
+ char *ethmod = bx_options.ne2k.Oethmod->get_choice(bx_options.ne2k.Oethmod->get());
+ BX_NE2K_THIS ethdev = eth_locator_c::create(ethmod,
+ bx_options.ne2k.Oethdev->getptr (),
+ (const char *) bx_options.ne2k.Omacaddr->getptr (),
+ rx_handler,
+ this);
+
+ if (BX_NE2K_THIS ethdev == NULL) {
+ BX_PANIC(("could not find eth module %s", ethmod));
+ // if they continue, use null.
+ BX_INFO(("could not find eth module %s - using null instead", ethmod));
+
+ BX_NE2K_THIS ethdev = eth_locator_c::create("null", NULL,
+ (const char *) bx_options.ne2k.Omacaddr->getptr (),
+ rx_handler,
+ this);
+ if (BX_NE2K_THIS ethdev == NULL)
+ BX_PANIC(("could not locate null module"));
+ }
+
+ // Bring the register state into power-up state
+ theNE2kDevice->reset(BX_RESET_HARDWARE);
+}
+
+#if BX_DEBUGGER
+
+/*
+ * this implements the info ne2k commands in the debugger.
+ * info ne2k - shows all registers
+ * info ne2k page N - shows all registers in a page
+ * info ne2k page N reg M - shows just one register
+ */
+
+#define SHOW_FIELD(reg,field) do { \
+ if (n>0 && !(n%5)) dbg_printf ("\n "); \
+ dbg_printf ("%s=%d ", #field, BX_NE2K_THIS s.reg.field); \
+ n++; \
+} while (0);
+#define BX_HIGH_BYTE(x) ((0xff00 & (x)) >> 8)
+#define BX_LOW_BYTE(x) (0x00ff & (x))
+#define BX_DUPLICATE(n) if (brief && num!=n) break;
+
+void
+bx_ne2k_c::print_info (FILE *fp, int page, int reg, int brief)
+{
+ int i;
+ int n = 0;
+ if (page < 0) {
+ for (page=0; page<=2; page++)
+ theNE2kDevice->print_info (fp, page, reg, 1);
+ // tell them how to use this command
+ dbg_printf ("\nHow to use the info ne2k command:\n");
+ dbg_printf ("info ne2k - show all registers\n");
+ dbg_printf ("info ne2k page N - show registers in page N\n");
+ dbg_printf ("info ne2k page N reg M - show just one register\n");
+ return;
+ }
+ if (page > 2) {
+ dbg_printf ("NE2K has only pages 0, 1, and 2. Page %d is out of range.\n", page);
+ return;
+ }
+ if (reg < 0) {
+ dbg_printf ("NE2K registers, page %d\n", page);
+ dbg_printf ("----------------------\n");
+ for (reg=0; reg<=15; reg++)
+ theNE2kDevice->print_info (fp, page, reg, 1);
+ dbg_printf ("----------------------\n");
+ return;
+ }
+ if (reg > 15) {
+ dbg_printf ("NE2K has only registers 0-15 (0x0-0xf). Register %d is out of range.\n", reg);
+ return;
+ }
+ if (!brief) {
+ dbg_printf ("NE2K Info - page %d, register 0x%02x\n", page, reg);
+ dbg_printf ("----------------------------------\n");
+ }
+ int num = page*0x100 + reg;
+ switch (num) {
+ case 0x0000:
+ case 0x0100:
+ case 0x0200:
+ dbg_printf ("CR (Command register):\n ");
+ SHOW_FIELD (CR, stop);
+ SHOW_FIELD (CR, start);
+ SHOW_FIELD (CR, tx_packet);
+ SHOW_FIELD (CR, rdma_cmd);
+ SHOW_FIELD (CR, pgsel);
+ dbg_printf ("\n");
+ break;
+ case 0x0003:
+ dbg_printf ("BNRY = Boundary Pointer = 0x%02x\n", BX_NE2K_THIS s.bound_ptr);
+ break;
+ case 0x0004:
+ dbg_printf ("TSR (Transmit Status Register), read-only:\n ");
+ SHOW_FIELD (TSR, tx_ok);
+ SHOW_FIELD (TSR, reserved);
+ SHOW_FIELD (TSR, collided);
+ SHOW_FIELD (TSR, aborted);
+ SHOW_FIELD (TSR, no_carrier);
+ SHOW_FIELD (TSR, fifo_ur);
+ SHOW_FIELD (TSR, cd_hbeat);
+ SHOW_FIELD (TSR, ow_coll);
+ dbg_printf ("\n");
+ // fall through into TPSR, no break line.
+ case 0x0204:
+ dbg_printf ("TPSR = Transmit Page Start = 0x%02x\n", BX_NE2K_THIS s.tx_page_start);
+ break;
+ case 0x0005:
+ case 0x0006: BX_DUPLICATE(0x0005);
+ dbg_printf ("NCR = Number of Collisions Register (read-only) = 0x%02x\n", BX_NE2K_THIS s.num_coll);
+ dbg_printf ("TBCR1,TBCR0 = Transmit Byte Count = %02x %02x\n",
+ BX_HIGH_BYTE (BX_NE2K_THIS s.tx_bytes),
+ BX_LOW_BYTE (BX_NE2K_THIS s.tx_bytes));
+ dbg_printf ("FIFO = %02x\n", BX_NE2K_THIS s.fifo);
+ break;
+ case 0x0007:
+ dbg_printf ("ISR (Interrupt Status Register):\n ");
+ SHOW_FIELD (ISR, pkt_rx);
+ SHOW_FIELD (ISR, pkt_tx);
+ SHOW_FIELD (ISR, rx_err);
+ SHOW_FIELD (ISR, tx_err);
+ SHOW_FIELD (ISR, overwrite);
+ SHOW_FIELD (ISR, cnt_oflow);
+ SHOW_FIELD (ISR, rdma_done);
+ SHOW_FIELD (ISR, reset);
+ dbg_printf ("\n");
+ break;
+ case 0x0008:
+ case 0x0009: BX_DUPLICATE(0x0008);
+ dbg_printf ("CRDA1,0 = Current remote DMA address = %02x %02x\n",
+ BX_HIGH_BYTE (BX_NE2K_THIS s.remote_dma),
+ BX_LOW_BYTE (BX_NE2K_THIS s.remote_dma));
+ dbg_printf ("RSAR1,0 = Remote start address = %02x %02x\n",
+ BX_HIGH_BYTE(s.remote_start),
+ BX_LOW_BYTE(s.remote_start));
+ break;
+ case 0x000a:
+ case 0x000b: BX_DUPLICATE(0x000a);
+ dbg_printf ("RCBR1,0 = Remote byte count = %02x\n", BX_NE2K_THIS s.remote_bytes);
+ break;
+ case 0x000c:
+ dbg_printf ("RSR (Receive Status Register), read-only:\n ");
+ SHOW_FIELD (RSR, rx_ok);
+ SHOW_FIELD (RSR, bad_crc);
+ SHOW_FIELD (RSR, bad_falign);
+ SHOW_FIELD (RSR, fifo_or);
+ SHOW_FIELD (RSR, rx_missed);
+ SHOW_FIELD (RSR, rx_mbit);
+ SHOW_FIELD (RSR, rx_disabled);
+ SHOW_FIELD (RSR, deferred);
+ dbg_printf ("\n");
+ // fall through into RCR
+ case 0x020c:
+ dbg_printf ("RCR (Receive Configuration Register):\n ");
+ SHOW_FIELD (RCR, errors_ok);
+ SHOW_FIELD (RCR, runts_ok);
+ SHOW_FIELD (RCR, broadcast);
+ SHOW_FIELD (RCR, multicast);
+ SHOW_FIELD (RCR, promisc);
+ SHOW_FIELD (RCR, monitor);
+ SHOW_FIELD (RCR, reserved);
+ dbg_printf ("\n");
+ break;
+ case 0x000d:
+ dbg_printf ("CNTR0 = Tally Counter 0 (Frame alignment errors) = %02x\n",
+ BX_NE2K_THIS s.tallycnt_0);
+ // fall through into TCR
+ case 0x020d:
+ dbg_printf ("TCR (Transmit Configuration Register):\n ");
+ SHOW_FIELD (TCR, crc_disable);
+ SHOW_FIELD (TCR, loop_cntl);
+ SHOW_FIELD (TCR, ext_stoptx);
+ SHOW_FIELD (TCR, coll_prio);
+ SHOW_FIELD (TCR, reserved);
+ dbg_printf ("\n");
+ break;
+ case 0x000e:
+ dbg_printf ("CNTR1 = Tally Counter 1 (CRC Errors) = %02x\n",
+ BX_NE2K_THIS s.tallycnt_1);
+ // fall through into DCR
+ case 0x020e:
+ dbg_printf ("DCR (Data Configuration Register):\n ");
+ SHOW_FIELD (DCR, wdsize);
+ SHOW_FIELD (DCR, endian);
+ SHOW_FIELD (DCR, longaddr);
+ SHOW_FIELD (DCR, loop);
+ SHOW_FIELD (DCR, auto_rx);
+ SHOW_FIELD (DCR, fifo_size);
+ dbg_printf ("\n");
+ break;
+ case 0x000f:
+ dbg_printf ("CNTR2 = Tally Counter 2 (Missed Packet Errors) = %02x\n",
+ BX_NE2K_THIS s.tallycnt_2);
+ // fall through into IMR
+ case 0x020f:
+ dbg_printf ("IMR (Interrupt Mask Register)\n ");
+ SHOW_FIELD (IMR, rx_inte);
+ SHOW_FIELD (IMR, tx_inte);
+ SHOW_FIELD (IMR, rxerr_inte);
+ SHOW_FIELD (IMR, txerr_inte);
+ SHOW_FIELD (IMR, overw_inte);
+ SHOW_FIELD (IMR, cofl_inte);
+ SHOW_FIELD (IMR, rdma_inte);
+ SHOW_FIELD (IMR, reserved);
+ dbg_printf ("\n");
+ break;
+ case 0x0101:
+ case 0x0102: BX_DUPLICATE(0x0101);
+ case 0x0103: BX_DUPLICATE(0x0101);
+ case 0x0104: BX_DUPLICATE(0x0101);
+ case 0x0105: BX_DUPLICATE(0x0101);
+ case 0x0106: BX_DUPLICATE(0x0101);
+ dbg_printf ("MAC address registers are located at page 1, registers 1-6.\n");
+ dbg_printf ("The MAC address is ");
+ for (i=0; i<=5; i++)
+ dbg_printf ("%02x%c", BX_NE2K_THIS s.physaddr[i], i<5?':' : '\n');
+ break;
+ case 0x0107:
+ dbg_printf ("Current page is 0x%02x\n", BX_NE2K_THIS s.curr_page);
+ break;
+ case 0x0108:
+ case 0x0109: BX_DUPLICATE(0x0108);
+ case 0x010A: BX_DUPLICATE(0x0108);
+ case 0x010B: BX_DUPLICATE(0x0108);
+ case 0x010C: BX_DUPLICATE(0x0108);
+ case 0x010D: BX_DUPLICATE(0x0108);
+ case 0x010E: BX_DUPLICATE(0x0108);
+ case 0x010F: BX_DUPLICATE(0x0108);
+ dbg_printf ("MAR0-7 (Multicast address registers 0-7) are set to:\n");
+ for (i=0; i<8; i++) dbg_printf ("%02x ", BX_NE2K_THIS s.mchash[i]);
+ dbg_printf ("\nMAR0 is listed first.\n");
+ break;
+ case 0x0001:
+ case 0x0002: BX_DUPLICATE(0x0001);
+ case 0x0201: BX_DUPLICATE(0x0001);
+ case 0x0202: BX_DUPLICATE(0x0001);
+ dbg_printf ("PSTART = Page start register = %02x\n", BX_NE2K_THIS s.page_start);
+ dbg_printf ("PSTOP = Page stop register = %02x\n", BX_NE2K_THIS s.page_stop);
+ dbg_printf ("Local DMA address = %02x %02x\n",
+ BX_HIGH_BYTE(BX_NE2K_THIS s.local_dma),
+ BX_LOW_BYTE(BX_NE2K_THIS s.local_dma));
+ break;
+ case 0x0203:
+ dbg_printf ("Remote Next Packet Pointer = %02x\n", BX_NE2K_THIS s.rempkt_ptr);
+ break;
+ case 0x0205:
+ dbg_printf ("Local Next Packet Pointer = %02x\n", BX_NE2K_THIS s.localpkt_ptr);
+ break;
+ case 0x0206:
+ case 0x0207: BX_DUPLICATE(0x0206);
+ dbg_printf ("Address Counter= %02x %02x\n",
+ BX_HIGH_BYTE(BX_NE2K_THIS s.address_cnt),
+ BX_LOW_BYTE(BX_NE2K_THIS s.address_cnt));
+ break;
+ case 0x0208:
+ case 0x0209: BX_DUPLICATE(0x0208);
+ case 0x020A: BX_DUPLICATE(0x0208);
+ case 0x020B: BX_DUPLICATE(0x0208);
+ if (!brief) dbg_printf ("Reserved\n");
+ case 0xffff:
+ dbg_printf ("IMR (Interrupt Mask Register):\n ");
+ dbg_printf ("\n");
+ break;
+ default:
+ dbg_printf ("NE2K info: sorry, page %d register %d cannot be displayed.\n", page, reg);
+ }
+ if (!brief)
+ dbg_printf ("\n");
+}
+
+#else
+
+void
+bx_ne2k_c::print_info (FILE *fp, int page, int reg, int brief)
+{
+}
+
+#endif
+
+#endif /* if BX_NE2K_SUPPORT */
diff --git a/tools/ioemu/iodev/ne2k.h b/tools/ioemu/iodev/ne2k.h
new file mode 100644
index 0000000000..37cc712106
--- /dev/null
+++ b/tools/ioemu/iodev/ne2k.h
@@ -0,0 +1,239 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: ne2k.h,v 1.11 2003/03/02 23:59:11 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Peter Grehan (grehan@iprg.nokia.com) coded all of this
+// NE2000/ether stuff.
+
+//
+// An implementation of an ne2000 ISA ethernet adapter. This part uses
+// a National Semiconductor DS-8390 ethernet MAC chip, with some h/w
+// to provide a windowed memory region for the chip and a MAC address.
+//
+
+
+#if BX_USE_NE2K_SMF
+# define BX_NE2K_SMF static
+# define BX_NE2K_THIS theNE2kDevice->
+#else
+# define BX_NE2K_SMF
+# define BX_NE2K_THIS this->
+#endif
+
+#define BX_NE2K_MEMSIZ (32*1024)
+#define BX_NE2K_MEMSTART (16*1024)
+#define BX_NE2K_MEMEND (BX_NE2K_MEMSTART + BX_NE2K_MEMSIZ)
+
+typedef struct {
+ //
+ // ne2k register state
+
+ //
+ // Page 0
+ //
+ // Command Register - 00h read/write
+ struct {
+ bx_bool stop; // STP - Software Reset command
+ bx_bool start; // START - start the NIC
+ bx_bool tx_packet; // TXP - initiate packet transmission
+ Bit8u rdma_cmd; // RD0,RD1,RD2 - Remote DMA command
+ Bit8u pgsel; // PS0,PS1 - Page select
+ } CR;
+ // Interrupt Status Register - 07h read/write
+ struct {
+ bx_bool pkt_rx; // PRX - packet received with no errors
+ bx_bool pkt_tx; // PTX - packet transmitted with no errors
+ bx_bool rx_err; // RXE - packet received with 1 or more errors
+ bx_bool tx_err; // TXE - packet tx'd " " " " "
+ bx_bool overwrite; // OVW - rx buffer resources exhausted
+ bx_bool cnt_oflow; // CNT - network tally counter MSB's set
+ bx_bool rdma_done; // RDC - remote DMA complete
+ bx_bool reset; // RST - reset status
+ } ISR;
+ // Interrupt Mask Register - 0fh write
+ struct {
+ bx_bool rx_inte; // PRXE - packet rx interrupt enable
+ bx_bool tx_inte; // PTXE - packet tx interrput enable
+ bx_bool rxerr_inte; // RXEE - rx error interrupt enable
+ bx_bool txerr_inte; // TXEE - tx error interrupt enable
+ bx_bool overw_inte; // OVWE - overwrite warn int enable
+ bx_bool cofl_inte; // CNTE - counter o'flow int enable
+ bx_bool rdma_inte; // RDCE - remote DMA complete int enable
+ bx_bool reserved; // D7 - reserved
+ } IMR;
+ // Data Configuration Register - 0eh write
+ struct {
+ bx_bool wdsize; // WTS - 8/16-bit select
+ bx_bool endian; // BOS - byte-order select
+ bx_bool longaddr; // LAS - long-address select
+ bx_bool loop; // LS - loopback select
+ bx_bool auto_rx; // AR - auto-remove rx packets with remote DMA
+ Bit8u fifo_size; // FT0,FT1 - fifo threshold
+ } DCR;
+ // Transmit Configuration Register - 0dh write
+ struct {
+ bx_bool crc_disable; // CRC - inhibit tx CRC
+ Bit8u loop_cntl; // LB0,LB1 - loopback control
+ bx_bool ext_stoptx; // ATD - allow tx disable by external mcast
+ bx_bool coll_prio; // OFST - backoff algorithm select
+ Bit8u reserved; // D5,D6,D7 - reserved
+ } TCR;
+ // Transmit Status Register - 04h read
+ struct {
+ bx_bool tx_ok; // PTX - tx complete without error
+ bx_bool reserved; // D1 - reserved
+ bx_bool collided; // COL - tx collided >= 1 times
+ bx_bool aborted; // ABT - aborted due to excessive collisions
+ bx_bool no_carrier; // CRS - carrier-sense lost
+ bx_bool fifo_ur; // FU - FIFO underrun
+ bx_bool cd_hbeat; // CDH - no tx cd-heartbeat from transceiver
+ bx_bool ow_coll; // OWC - out-of-window collision
+ } TSR;
+ // Receive Configuration Register - 0ch write
+ struct {
+ bx_bool errors_ok; // SEP - accept pkts with rx errors
+ bx_bool runts_ok; // AR - accept < 64-byte runts
+ bx_bool broadcast; // AB - accept eth broadcast address
+ bx_bool multicast; // AM - check mcast hash array
+ bx_bool promisc; // PRO - accept all packets
+ bx_bool monitor; // MON - check pkts, but don't rx
+ Bit8u reserved; // D6,D7 - reserved
+ } RCR;
+ // Receive Status Register - 0ch read
+ struct {
+ bx_bool rx_ok; // PRX - rx complete without error
+ bx_bool bad_crc; // CRC - Bad CRC detected
+ bx_bool bad_falign; // FAE - frame alignment error
+ bx_bool fifo_or; // FO - FIFO overrun
+ bx_bool rx_missed; // MPA - missed packet error
+ bx_bool rx_mbit; // PHY - unicast or mcast/bcast address match
+ bx_bool rx_disabled; // DIS - set when in monitor mode
+ bx_bool deferred; // DFR - collision active
+ } RSR;
+
+ Bit16u local_dma; // 01,02h read ; current local DMA addr
+ Bit8u page_start; // 01h write ; page start register
+ Bit8u page_stop; // 02h write ; page stop register
+ Bit8u bound_ptr; // 03h read/write ; boundary pointer
+ Bit8u tx_page_start; // 04h write ; transmit page start register
+ Bit8u num_coll; // 05h read ; number-of-collisions register
+ Bit16u tx_bytes; // 05,06h write ; transmit byte-count register
+ Bit8u fifo; // 06h read ; FIFO
+ Bit16u remote_dma; // 08,09h read ; current remote DMA addr
+ Bit16u remote_start; // 08,09h write ; remote start address register
+ Bit16u remote_bytes; // 0a,0bh write ; remote byte-count register
+ Bit8u tallycnt_0; // 0dh read ; tally counter 0 (frame align errors)
+ Bit8u tallycnt_1; // 0eh read ; tally counter 1 (CRC errors)
+ Bit8u tallycnt_2; // 0fh read ; tally counter 2 (missed pkt errors)
+
+ //
+ // Page 1
+ //
+ // Command Register 00h (repeated)
+ //
+ Bit8u physaddr[6]; // 01-06h read/write ; MAC address
+ Bit8u curr_page; // 07h read/write ; current page register
+ Bit8u mchash[8]; // 08-0fh read/write ; multicast hash array
+
+ //
+ // Page 2 - diagnostic use only
+ //
+ // Command Register 00h (repeated)
+ //
+ // Page Start Register 01h read (repeated)
+ // Page Stop Register 02h read (repeated)
+ // Current Local DMA Address 01,02h write (repeated)
+ // Transmit Page start address 04h read (repeated)
+ // Receive Configuration Register 0ch read (repeated)
+ // Transmit Configuration Register 0dh read (repeated)
+ // Data Configuration Register 0eh read (repeated)
+ // Interrupt Mask Register 0fh read (repeated)
+ //
+ Bit8u rempkt_ptr; // 03h read/write ; remote next-packet pointer
+ Bit8u localpkt_ptr; // 05h read/write ; local next-packet pointer
+ Bit16u address_cnt; // 06,07h read/write ; address counter
+
+ //
+ // Page 3 - should never be modified.
+ //
+
+ // Novell ASIC state
+ Bit8u macaddr[32]; // ASIC ROM'd MAC address, even bytes
+ Bit8u mem[BX_NE2K_MEMSIZ]; // on-chip packet memory
+
+ // ne2k internal state
+ Bit32u base_address;
+ int base_irq;
+ int tx_timer_index;
+ int tx_timer_active;
+
+} bx_ne2k_t;
+
+
+
+class bx_ne2k_c : public bx_ne2k_stub_c {
+public:
+ bx_ne2k_c(void);
+ ~bx_ne2k_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+ virtual void print_info (FILE *file, int page, int reg, int nodups);
+
+private:
+ bx_ne2k_t s;
+
+ eth_pktmover_c *ethdev;
+
+ BX_NE2K_SMF Bit32u read_cr(void);
+ BX_NE2K_SMF void write_cr(Bit32u value);
+
+ BX_NE2K_SMF Bit32u chipmem_read(Bit32u address, unsigned io_len) BX_CPP_AttrRegparmN(2);
+ BX_NE2K_SMF Bit32u asic_read(Bit32u offset, unsigned io_len) BX_CPP_AttrRegparmN(2);
+ BX_NE2K_SMF Bit32u page0_read(Bit32u offset, unsigned io_len);
+ BX_NE2K_SMF Bit32u page1_read(Bit32u offset, unsigned io_len);
+ BX_NE2K_SMF Bit32u page2_read(Bit32u offset, unsigned io_len);
+ BX_NE2K_SMF Bit32u page3_read(Bit32u offset, unsigned io_len);
+
+ BX_NE2K_SMF void chipmem_write(Bit32u address, Bit32u value, unsigned io_len) BX_CPP_AttrRegparmN(3);
+ BX_NE2K_SMF void asic_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page0_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page1_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page2_write(Bit32u address, Bit32u value, unsigned io_len);
+ BX_NE2K_SMF void page3_write(Bit32u address, Bit32u value, unsigned io_len);
+
+ static void tx_timer_handler(void *);
+ BX_NE2K_SMF void tx_timer(void);
+
+ static void rx_handler(void *arg, const void *buf, unsigned len);
+ BX_NE2K_SMF unsigned mcast_index(const void *dst);
+ BX_NE2K_SMF void rx_frame(const void *buf, unsigned io_len);
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_NE2K_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+};
diff --git a/tools/ioemu/iodev/osdep.cc b/tools/ioemu/iodev/osdep.cc
new file mode 100644
index 0000000000..c010306c82
--- /dev/null
+++ b/tools/ioemu/iodev/osdep.cc
@@ -0,0 +1,340 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: osdep.cc,v 1.14.2.1 2004/02/06 22:14:34 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// osdep.cc
+//
+// Provide definition of library functions that are missing on various
+// systems. The only reason this is a .cc file rather than a .c file
+// is so that it can include bochs.h. Bochs.h includes all the required
+// system headers, with appropriate #ifdefs for different compilers and
+// platforms.
+//
+
+#include "bochs.h"
+
+//////////////////////////////////////////////////////////////////////
+// Missing library functions. These should work on any platform
+// that needs them.
+//////////////////////////////////////////////////////////////////////
+
+#if !BX_HAVE_SNPRINTF
+/* XXX use real snprintf */
+/* if they don't have snprintf, just use sprintf */
+int bx_snprintf (char *s, size_t maxlen, const char *format, ...)
+{
+ va_list arg;
+ int done;
+
+ va_start (arg, format);
+ done = vsprintf (s, format, arg);
+ va_end (arg);
+
+ return done;
+}
+#endif /* !BX_HAVE_SNPRINTF */
+
+
+#if (!BX_HAVE_STRTOULL && !BX_HAVE_STRTOUQ)
+/* taken from glibc-2.2.2: strtod.c, and stripped down a lot. There are
+ still a few leftover references to decimal points and exponents,
+ but it works for bases 10 and 16 */
+
+#define RETURN(val,end) \
+ do { if (endptr != NULL) *endptr = (char *) (end); \
+ return val; } while (0)
+
+Bit64u
+bx_strtoull (const char *nptr, char **endptr, int baseignore)
+{
+ int negative; /* The sign of the number. */
+ int exponent; /* Exponent of the number. */
+
+ /* Numbers starting `0X' or `0x' have to be processed with base 16. */
+ int base = 10;
+
+ /* Number of bits currently in result value. */
+ int bits;
+
+ /* Running pointer after the last character processed in the string. */
+ const char *cp, *tp;
+ /* Start of significant part of the number. */
+ const char *startp, *start_of_digits;
+ /* Total number of digit and number of digits in integer part. */
+ int dig_no;
+ /* Contains the last character read. */
+ char c;
+
+ Bit64s n = 0;
+ char const *p;
+
+ /* Prepare number representation. */
+ exponent = 0;
+ negative = 0;
+ bits = 0;
+
+ /* Parse string to get maximal legal prefix. We need the number of
+ characters of the integer part, the fractional part and the exponent. */
+ cp = nptr - 1;
+ /* Ignore leading white space. */
+ do
+ c = *++cp;
+ while (isspace (c));
+
+ /* Get sign of the result. */
+ if (c == '-')
+ {
+ negative = 1;
+ c = *++cp;
+ }
+ else if (c == '+')
+ c = *++cp;
+
+ if (c < '0' || c > '9')
+ {
+ /* It is really a text we do not recognize. */
+ RETURN (0, nptr);
+ }
+
+ /* First look whether we are faced with a hexadecimal number. */
+ if (c == '0' && tolower (cp[1]) == 'x')
+ {
+ /* Okay, it is a hexa-decimal number. Remember this and skip
+ the characters. BTW: hexadecimal numbers must not be
+ grouped. */
+ base = 16;
+ cp += 2;
+ c = *cp;
+ }
+
+ /* Record the start of the digits, in case we will check their grouping. */
+ start_of_digits = startp = cp;
+
+ /* Ignore leading zeroes. This helps us to avoid useless computations. */
+ while (c == '0')
+ c = *++cp;
+
+ /* If no other digit but a '0' is found the result is 0.0.
+ Return current read pointer. */
+ if ((c < '0' || c > '9')
+ && (base == 16 && (c < tolower ('a') || c > tolower ('f')))
+ && (base == 16 && (cp == start_of_digits || tolower (c) != 'p'))
+ && (base != 16 && tolower (c) != 'e'))
+ {
+ tp = start_of_digits;
+ /* If TP is at the start of the digits, there was no correctly
+ grouped prefix of the string; so no number found. */
+ RETURN (0, tp == start_of_digits ? (base == 16 ? cp - 1 : nptr) : tp);
+ }
+
+ /* Remember first significant digit and read following characters until the
+ decimal point, exponent character or any non-FP number character. */
+ startp = cp;
+ dig_no = 0;
+ while (1)
+ {
+ if ((c >= '0' && c <= '9')
+ || (base == 16 && tolower (c) >= 'a' && tolower (c) <= 'f'))
+ ++dig_no;
+ else
+ break;
+ c = *++cp;
+ }
+
+ /* The whole string is parsed. Store the address of the next character. */
+ if (endptr)
+ *endptr = (char *) cp;
+
+ if (dig_no == 0)
+ return 0;
+
+ for (p=start_of_digits; p!=cp; p++) {
+ n = n * (Bit64s)base;
+ c = tolower (*p);
+ c = (c >= 'a') ? (10+c-'a') : c-'0';
+ n = n + (Bit64s)c;
+ //printf ("after shifting in digit %c, n is %lld\n", *p, n);
+ }
+ return negative? -n : n;
+}
+#endif /* !BX_HAVE_STRTOULL */
+
+#if BX_TEST_STRTOULL_MAIN
+/* test driver for strtoull. Do not compile by default. */
+int main (int argc, char **argv)
+{
+ char buf[256], *endbuf;
+ long l;
+ Bit64s ll;
+ while (1) {
+ printf ("Enter a long int: ");
+ gets (buf);
+ l = strtoul (buf, &endbuf, 10);
+ printf ("As a long, %ld\n", l);
+ printf ("Endbuf is at buf[%d]\n", endbuf-buf);
+ ll = bx_strtoull (buf, &endbuf, 10);
+ printf ("As a long long, %lld\n", ll);
+ printf ("Endbuf is at buf[%d]\n", endbuf-buf);
+ }
+ return 0;
+}
+#endif /* BX_TEST_STRTOULL_MAIN */
+
+#if !BX_HAVE_STRDUP
+/* XXX use real strdup */
+char *bx_strdup(const char *str)
+{
+ char *temp;
+
+ temp = (char*)malloc(strlen(str)+1);
+ sprintf(temp, "%s", str);
+ return temp;
+
+ // Well, I'm sure this isn't how strdup is REALLY implemented,
+ // but it works...
+}
+#endif /* !BX_HAVE_STRDUP */
+
+#if !BX_HAVE_STRREV
+char *bx_strrev(char *str)
+{
+ char *p1, *p2;
+
+ if (! str || ! *str)
+ return str;
+
+ for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) {
+ *p1 ^= *p2;
+ *p2 ^= *p1;
+ *p1 ^= *p2;
+ }
+ return str;
+}
+#endif /* !BX_HAVE_STRREV */
+
+#if BX_WITH_MACOS
+namespace std{extern "C" {char *mktemp(char *tpl);}}
+#endif
+#if !BX_HAVE_MKSTEMP
+int bx_mkstemp(char *tpl)
+{
+ mktemp(tpl);
+ return ::open(tpl, O_RDWR | O_CREAT | O_TRUNC
+# ifdef O_BINARY
+ | O_BINARY
+# endif
+ , S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP);
+}
+#endif // !BX_HAVE_MKSTEMP
+
+//////////////////////////////////////////////////////////////////////
+// Missing library functions, implemented for MacOS only
+//////////////////////////////////////////////////////////////////////
+
+#if BX_WITH_MACOS
+// these functions are part of MacBochs. They are not intended to be
+// portable!
+#include <Devices.h>
+#include <Files.h>
+#include <Disks.h>
+
+int fd_read(char *buffer, Bit32u offset, Bit32u bytes)
+{
+ OSErr err;
+ IOParam param;
+
+ param.ioRefNum=-5; // Refnum of the floppy disk driver
+ param.ioVRefNum=1;
+ param.ioPosMode=fsFromStart;
+ param.ioPosOffset=offset;
+ param.ioBuffer=buffer;
+ param.ioReqCount=bytes;
+ err = PBReadSync((union ParamBlockRec *)(&param));
+ return param.ioActCount;
+}
+
+int fd_write(char *buffer, Bit32u offset, Bit32u bytes)
+{
+ OSErr err;
+ IOParam param;
+
+ param.ioRefNum=-5; // Refnum of the floppy disk driver
+ param.ioVRefNum=1;
+ param.ioPosMode=fsFromStart;
+ param.ioPosOffset=offset;
+ param.ioBuffer=buffer;
+ param.ioReqCount=bytes;
+ err = PBWriteSync((union ParamBlockRec *)(&param));
+ return param.ioActCount;
+}
+
+int fd_stat(struct stat *buf)
+{
+ OSErr err;
+ DrvSts status;
+ int result;
+
+ result = 0;
+ err = DriveStatus(1, &status);
+ if (status.diskInPlace <1 || status.diskInPlace > 2)
+ result = -1;
+ buf->st_mode = S_IFCHR;
+ return result;
+}
+#endif /* BX_WITH_MACOS */
+
+
+
+//////////////////////////////////////////////////////////////////////
+// New functions to replace library functions
+// with OS-independent versions
+//////////////////////////////////////////////////////////////////////
+
+#if BX_HAVE_REALTIME_USEC
+# if BX_HAVE_GETTIMEOFDAY
+Bit64u bx_get_realtime64_usec (void) {
+ timeval thetime;
+ gettimeofday(&thetime,0);
+ Bit64u mytime;
+ mytime=(Bit64u)thetime.tv_sec*(Bit64u)1000000+(Bit64u)thetime.tv_usec;
+ return mytime;
+}
+# elif defined(WIN32)
+Bit64u last_realtime64_top = 0;
+Bit64u last_realtime64_bottom = 0;
+Bit64u bx_get_realtime64_usec (void) {
+ Bit64u new_bottom = ((Bit64u) GetTickCount()) & BX_CONST64(0x0FFFFFFFF);
+ if(new_bottom < last_realtime64_bottom) {
+ last_realtime64_top += BX_CONST64(0x0000000100000000);
+ }
+ last_realtime64_bottom = new_bottom;
+ Bit64u interim_realtime64 =
+ (last_realtime64_top & BX_CONST64(0xFFFFFFFF00000000)) |
+ (new_bottom & BX_CONST64(0x00000000FFFFFFFF));
+ return interim_realtime64*(BX_CONST64(1000));
+}
+# endif
+#endif
diff --git a/tools/ioemu/iodev/parallel.cc b/tools/ioemu/iodev/parallel.cc
new file mode 100644
index 0000000000..8b0d4b3245
--- /dev/null
+++ b/tools/ioemu/iodev/parallel.cc
@@ -0,0 +1,300 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: parallel.cc,v 1.24 2003/10/29 17:29:26 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+////////////////////////////////////////////////////////
+// This code was just a few stubs until Volker.Ruppert@t-online.de
+// fixed it up in November 2001.
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#define LOG_THIS theParallelDevice->
+
+bx_parallel_c *theParallelDevice = NULL;
+
+ int
+libparallel_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theParallelDevice = new bx_parallel_c ();
+ bx_devices.pluginParallelDevice = theParallelDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theParallelDevice, BX_PLUGIN_PARALLEL);
+ return(0); // Success
+}
+
+ void
+libparallel_LTX_plugin_fini(void)
+{
+}
+
+bx_parallel_c::bx_parallel_c(void)
+{
+ put("PAR");
+ settype(PARLOG);
+ s.output = NULL;
+}
+
+bx_parallel_c::~bx_parallel_c(void)
+{
+ if (s.output != NULL)
+ fclose(s.output);
+}
+
+ void
+bx_parallel_c::init(void)
+{
+ BX_DEBUG(("Init $Id: parallel.cc,v 1.24 2003/10/29 17:29:26 vruppert Exp $"));
+
+ if (bx_options.par[0].Oenabled->get ()) {
+
+ /* PARALLEL PORT 1 */
+
+ DEV_register_irq(7, "Parallel Port 1");
+ BX_INFO (("parallel port 1 at 0x378 irq 7"));
+ for (unsigned addr=0x0378; addr<=0x037A; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "Parallel Port 1", 1);
+ }
+ DEV_register_iowrite_handler(this, write_handler, 0x0378, "Parallel Port 1", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x037A, "Parallel Port 1", 1);
+
+ BX_PAR_THIS s.STATUS.error = 1;
+ BX_PAR_THIS s.STATUS.slct = 1;
+ BX_PAR_THIS s.STATUS.pe = 0;
+ BX_PAR_THIS s.STATUS.ack = 1;
+ BX_PAR_THIS s.STATUS.busy = 1;
+
+ BX_PAR_THIS s.CONTROL.strobe = 0;
+ BX_PAR_THIS s.CONTROL.autofeed = 0;
+ BX_PAR_THIS s.CONTROL.init = 1;
+ BX_PAR_THIS s.CONTROL.slct_in = 1;
+ BX_PAR_THIS s.CONTROL.irq = 0;
+ BX_PAR_THIS s.CONTROL.input = 0;
+
+ BX_PAR_THIS s.initmode = 0;
+
+ if (strlen(bx_options.par[0].Ooutfile->getptr ()) > 0) {
+ s.output = fopen(bx_options.par[0].Ooutfile->getptr (), "wb");
+ if (!s.output)
+ BX_PANIC (("Could not open '%s' to write parport1 output",
+ bx_options.par[0].Ooutfile->getptr ()));
+ }
+ }
+}
+
+ void
+bx_parallel_c::reset(unsigned type)
+{
+}
+
+ void
+bx_parallel_c::virtual_printer(void)
+{
+ if (BX_PAR_THIS s.STATUS.slct) {
+ if (BX_PAR_THIS s.output != NULL) {
+ fputc(BX_PAR_THIS s.data, BX_PAR_THIS s.output);
+ fflush (BX_PAR_THIS s.output);
+ }
+ if (BX_PAR_THIS s.CONTROL.irq == 1) {
+ DEV_pic_raise_irq(7);
+ }
+ BX_PAR_THIS s.STATUS.ack = 0;
+ BX_PAR_THIS s.STATUS.busy = 1;
+ }
+ else {
+ BX_ERROR(("data is valid, but printer is offline"));
+ }
+}
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_parallel_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_PAR_SMF
+ bx_parallel_c *class_ptr = (bx_parallel_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_parallel_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PAR_SMF
+
+ Bit32u retval;
+
+ switch (address) {
+ /* PARALLEL PORT 1 */
+ case 0x0378:
+ if (!BX_PAR_THIS s.CONTROL.input) {
+ return (Bit32u)BX_PAR_THIS s.data;
+ } else {
+ BX_ERROR(("read: input mode not supported"));
+ return (0xFF);
+ }
+ break;
+ case 0x0379:
+ {
+ retval = ((BX_PAR_THIS s.STATUS.busy << 7) |
+ (BX_PAR_THIS s.STATUS.ack << 6) |
+ (BX_PAR_THIS s.STATUS.pe << 5) |
+ (BX_PAR_THIS s.STATUS.slct << 4) |
+ (BX_PAR_THIS s.STATUS.error << 3));
+ if (BX_PAR_THIS s.STATUS.ack == 0) {
+ BX_PAR_THIS s.STATUS.ack = 1;
+ if (BX_PAR_THIS s.CONTROL.irq == 1) {
+ DEV_pic_lower_irq(7);
+ }
+ }
+ if (BX_PAR_THIS s.initmode == 1) {
+ BX_PAR_THIS s.STATUS.busy = 1;
+ BX_PAR_THIS s.STATUS.slct = 1;
+ BX_PAR_THIS s.STATUS.ack = 0;
+ if (BX_PAR_THIS s.CONTROL.irq == 1) {
+ DEV_pic_raise_irq(7);
+ }
+ BX_PAR_THIS s.initmode = 0;
+ }
+ BX_DEBUG(("read: status register returns 0x%02x", retval));
+ return retval;
+ }
+ break;
+ case 0x037A:
+ {
+ retval = ((BX_PAR_THIS s.CONTROL.input << 5) |
+ (BX_PAR_THIS s.CONTROL.irq << 4) |
+ (BX_PAR_THIS s.CONTROL.slct_in << 3) |
+ (BX_PAR_THIS s.CONTROL.init << 2) |
+ (BX_PAR_THIS s.CONTROL.autofeed << 1) |
+ (BX_PAR_THIS s.CONTROL.strobe));
+ BX_DEBUG(("read: control register returns 0x%02x", retval));
+ return retval;
+ }
+ break;
+ }
+ return(0);
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_parallel_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PAR_SMF
+ bx_parallel_c *class_ptr = (bx_parallel_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_parallel_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PAR_SMF
+
+ switch (address) {
+ /* PARALLEL PORT 1 */
+ case 0x0378:
+ BX_PAR_THIS s.data = (Bit8u)value;
+ BX_DEBUG(("write: data output register = 0x%02x", (Bit8u)value));
+ break;
+ case 0x037A:
+ {
+ if ((value & 0x01) == 0x01) {
+ if (BX_PAR_THIS s.CONTROL.strobe == 0) {
+ BX_PAR_THIS s.CONTROL.strobe = 1;
+ virtual_printer(); // data is valid now
+ }
+ } else {
+ if (BX_PAR_THIS s.CONTROL.strobe == 1) {
+ BX_PAR_THIS s.CONTROL.strobe = 0;
+ }
+ }
+ BX_PAR_THIS s.CONTROL.autofeed = ((value & 0x02) == 0x02);
+ if ((value & 0x04) == 0x04) {
+ if (BX_PAR_THIS s.CONTROL.init == 0) {
+ BX_PAR_THIS s.CONTROL.init = 1;
+ BX_PAR_THIS s.STATUS.busy = 0;
+ BX_PAR_THIS s.STATUS.slct = 0;
+ BX_PAR_THIS s.initmode = 1;
+ BX_DEBUG(("printer init requested"));
+ }
+ } else {
+ if (BX_PAR_THIS s.CONTROL.init == 1) {
+ BX_PAR_THIS s.CONTROL.init = 0;
+ }
+ }
+ if ((value & 0x08) == 0x08) {
+ if (BX_PAR_THIS s.CONTROL.slct_in == 0) {
+ BX_PAR_THIS s.CONTROL.slct_in = 1;
+ BX_DEBUG(("printer now online"));
+ }
+ } else {
+ if (BX_PAR_THIS s.CONTROL.slct_in == 1) {
+ BX_PAR_THIS s.CONTROL.slct_in = 0;
+ BX_DEBUG(("printer now offline"));
+ }
+ }
+ BX_PAR_THIS s.STATUS.slct = BX_PAR_THIS s.CONTROL.slct_in;
+ if ((value & 0x10) == 0x10) {
+ if (BX_PAR_THIS s.CONTROL.irq == 0) {
+ BX_PAR_THIS s.CONTROL.irq = 1;
+ BX_DEBUG(("irq mode selected"));
+ }
+ } else {
+ if (BX_PAR_THIS s.CONTROL.irq == 1) {
+ BX_PAR_THIS s.CONTROL.irq = 0;
+ BX_DEBUG(("polling mode selected"));
+ }
+ }
+ if ((value & 0x20) == 0x20) {
+ if (BX_PAR_THIS s.CONTROL.input == 0) {
+ BX_PAR_THIS s.CONTROL.input = 1;
+ BX_DEBUG(("data input mode selected"));
+ }
+ } else {
+ if (BX_PAR_THIS s.CONTROL.input == 1) {
+ BX_PAR_THIS s.CONTROL.input = 0;
+ BX_DEBUG(("data output mode selected"));
+ }
+ }
+ if ((value & 0xC0) > 0) {
+ BX_ERROR(("write: unsupported control bit ignored"));
+ }
+ }
+ break;
+ }
+}
diff --git a/tools/ioemu/iodev/parallel.h b/tools/ioemu/iodev/parallel.h
new file mode 100644
index 0000000000..b4a256a616
--- /dev/null
+++ b/tools/ioemu/iodev/parallel.h
@@ -0,0 +1,78 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: parallel.h,v 1.11 2002/10/25 11:44:40 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#if BX_USE_PAR_SMF
+# define BX_PAR_SMF static
+# define BX_PAR_THIS theParallelDevice->
+#else
+# define BX_PAR_SMF
+# define BX_PAR_THIS this->
+#endif
+
+typedef struct {
+ Bit8u data;
+ struct {
+ bx_bool error;
+ bx_bool slct;
+ bx_bool pe;
+ bx_bool ack;
+ bx_bool busy;
+ } STATUS;
+ struct {
+ bx_bool strobe;
+ bx_bool autofeed;
+ bx_bool init;
+ bx_bool slct_in;
+ bx_bool irq;
+ bx_bool input;
+ } CONTROL;
+ FILE *output;
+ bx_bool initmode;
+} bx_par_t;
+
+
+
+class bx_parallel_c : public bx_devmodel_c {
+public:
+
+ bx_parallel_c(void);
+ ~bx_parallel_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+
+private:
+ bx_par_t s;
+
+ static void virtual_printer();
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_PAR_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+ };
diff --git a/tools/ioemu/iodev/pc_system.cc b/tools/ioemu/iodev/pc_system.cc
new file mode 100644
index 0000000000..de34d9914b
--- /dev/null
+++ b/tools/ioemu/iodev/pc_system.cc
@@ -0,0 +1,570 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pc_system.cc,v 1.34 2003/06/07 19:16:51 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+#include "bochs.h"
+#define LOG_THIS bx_pc_system.
+
+#ifdef WIN32
+#ifndef __MINGW32__
+// #include <winsock2.h> // +++
+#include <winsock.h>
+#endif
+#endif
+
+#if BX_SHOW_IPS
+unsigned long ips_count=0;
+#endif
+
+#if defined(PROVIDE_M_IPS)
+double m_ips; // Millions of Instructions Per Second
+#endif
+
+#ifdef BX_USE_VMX
+unsigned int tsc_per_bx_tick;
+#endif
+
+// Option for turning off BX_TIMER_DEBUG?
+// Check out m_ips and ips
+
+#define SpewPeriodicTimerInfo 0
+#define MinAllowableTimerPeriod 1
+
+
+#if SpewPeriodicTimerInfo
+// If debugging, set the heartbeat to 5M cycles. Each heartbeat
+// spews the active timer info.
+const Bit64u bx_pc_system_c::NullTimerInterval = 5000000;
+#else
+// This must be the maximum 32-bit unsigned int value, NOT (Bit64u) -1.
+const Bit64u bx_pc_system_c::NullTimerInterval = 0xffffffff;
+#endif
+
+ // constructor
+bx_pc_system_c::bx_pc_system_c(void)
+{
+ this->put("SYS");
+
+ // Timer[0] is the null timer. It is initialized as a special
+ // case here. It should never be turned off or modified, and its
+ // duration should always remain the same.
+ ticksTotal = 0; // Reset ticks since emulator started.
+ timer[0].period = NullTimerInterval;
+ timer[0].timeToFire = ticksTotal + NullTimerInterval;
+ timer[0].active = 1;
+ timer[0].continuous = 1;
+ timer[0].funct = nullTimer;
+ timer[0].this_ptr = this;
+ currCountdown = NullTimerInterval;
+ currCountdownPeriod = NullTimerInterval;
+ numTimers = 1; // So far, only the nullTimer.
+ lastTimeUsec = 0;
+ usecSinceLast = 0;
+}
+
+ void
+bx_pc_system_c::init_ips(Bit32u ips)
+{
+ HRQ = 0;
+
+ enable_a20 = 1;
+ //set_INTR (0);
+
+#if BX_CPU_LEVEL < 2
+ a20_mask = 0xfffff;
+#elif BX_CPU_LEVEL == 2
+ a20_mask = 0xffffff;
+#else /* 386+ */
+ a20_mask = 0xffffffff;
+#endif
+
+#ifdef BX_USE_VMX
+ Bit64u phy_cpu_freq = cpu_calibrate_ticks();
+
+ if (ips == 500000) { //default ips: we use fixed scaling factor to calulate ips
+ tsc_per_bx_tick = 2000;
+ ips = phy_cpu_freq / tsc_per_bx_tick;
+ } else //use uesr defined ips to calulate factor
+ tsc_per_bx_tick = ((phy_cpu_freq + (ips>>1)) / ips);
+#endif
+
+ // parameter 'ips' is the processor speed in Instructions-Per-Second
+ m_ips = double(ips) / 1000000.0L;
+
+ BX_DEBUG(("ips = %u", (unsigned) ips));
+}
+
+ void
+bx_pc_system_c::set_HRQ(bx_bool val)
+{
+ HRQ = val;
+ if (val)
+ BX_CPU(0)->async_event = 1;
+}
+
+
+#if (BX_NUM_SIMULATORS < 2)
+ void
+bx_pc_system_c::set_INTR(bx_bool value)
+{
+ if (bx_dbg.interrupts)
+ BX_INFO(("pc_system: Setting INTR=%d on bootstrap processor %d", (int)value, BX_BOOTSTRAP_PROCESSOR));
+ //INTR = value;
+ BX_CPU(BX_BOOTSTRAP_PROCESSOR)->set_INTR(value);
+}
+#endif
+
+//
+// Read from the IO memory address space
+//
+
+ Bit32u BX_CPP_AttrRegparmN(2)
+bx_pc_system_c::inp(Bit16u addr, unsigned io_len)
+{
+ Bit32u ret;
+
+ ret = bx_devices.inp(addr, io_len);
+
+ return( ret );
+}
+
+
+//
+// Write to the IO memory address space.
+//
+
+ void BX_CPP_AttrRegparmN(3)
+bx_pc_system_c::outp(Bit16u addr, Bit32u value, unsigned io_len)
+{
+ bx_devices.outp(addr, value, io_len);
+}
+
+ void BX_CPP_AttrRegparmN(1)
+bx_pc_system_c::set_enable_a20(Bit8u value)
+{
+#if BX_CPU_LEVEL < 2
+ BX_PANIC(("set_enable_a20() called: 8086 emulation"));
+#else
+
+#if BX_SUPPORT_A20
+ unsigned old_enable_a20 = enable_a20;
+
+ if (value) {
+ enable_a20 = 1;
+#if BX_CPU_LEVEL == 2
+ a20_mask = 0xffffff; /* 286: enable all 24 address lines */
+#else /* 386+ */
+ a20_mask = 0xffffffff; /* 386: enable all 32 address lines */
+#endif
+ }
+ else {
+ enable_a20 = 0;
+ a20_mask = 0xffefffff; /* mask off A20 address line */
+ }
+
+ BX_DBG_A20_REPORT(value);
+
+ BX_DEBUG(("A20: set() = %u", (unsigned) enable_a20));
+
+ // If there has been a transition, we need to notify the CPUs so
+ // they can potentially invalidate certain cache info based on
+ // A20-line-applied physical addresses.
+ if (old_enable_a20 != enable_a20) {
+ for (unsigned i=0; i<BX_SMP_PROCESSORS; i++)
+ BX_CPU(i)->pagingA20Changed();
+ }
+#else
+ BX_DEBUG(("set_enable_a20: ignoring: SUPPORT_A20 = 0"));
+#endif // #if BX_SUPPORT_A20
+
+#endif
+}
+
+ bx_bool
+bx_pc_system_c::get_enable_a20(void)
+{
+#if BX_SUPPORT_A20
+ if (bx_dbg.a20)
+ BX_INFO(("A20: get() = %u", (unsigned) enable_a20));
+
+ if (enable_a20) return(1);
+ else return(0);
+#else
+ BX_INFO(("get_enable_a20: ignoring: SUPPORT_A20 = 0"));
+ return(1);
+#endif // #if BX_SUPPORT_A20
+}
+
+ int
+bx_pc_system_c::ResetSignal( PCS_OP operation )
+{
+ UNUSED( operation );
+ // Reset the processor.
+
+ BX_ERROR(( "# bx_pc_system_c::ResetSignal() called" ));
+ for (int i=0; i<BX_SMP_PROCESSORS; i++)
+ BX_CPU(i)->reset(BX_RESET_SOFTWARE);
+ DEV_reset_devices(BX_RESET_SOFTWARE);
+ return(0);
+}
+
+
+ Bit8u
+bx_pc_system_c::IAC(void)
+{
+ return( DEV_pic_iac() );
+}
+
+ void
+bx_pc_system_c::exit(void)
+{
+ if (DEV_hd_present())
+ DEV_hd_close_harddrive();
+
+ BX_INFO(("Last time is %u", (unsigned) DEV_cmos_get_timeval()));
+
+ if (bx_gui) bx_gui->exit();
+}
+
+
+// ================================================
+// Bochs internal timer delivery framework features
+// ================================================
+
+ int
+bx_pc_system_c::register_timer( void *this_ptr, void (*funct)(void *),
+ Bit32u useconds, bx_bool continuous, bx_bool active, const char *id)
+{
+ Bit64u ticks;
+
+ // Convert useconds to number of ticks.
+ ticks = (Bit64u) (double(useconds) * m_ips);
+
+ return register_timer_ticks(this_ptr, funct, ticks, continuous, active, id);
+}
+
+ int
+bx_pc_system_c::register_timer_ticks(void* this_ptr, bx_timer_handler_t funct,
+ Bit64u ticks, bx_bool continuous, bx_bool active, const char *id)
+{
+ unsigned i;
+
+#if BX_TIMER_DEBUG
+ if (numTimers >= BX_MAX_TIMERS) {
+ BX_PANIC(("register_timer: too many registered timers."));
+ }
+ if (this_ptr == NULL)
+ BX_PANIC(("register_timer_ticks: this_ptr is NULL"));
+ if (funct == NULL)
+ BX_PANIC(("register_timer_ticks: funct is NULL"));
+#endif
+
+ // If the timer frequency is rediculously low, make it more sane.
+ // This happens when 'ips' is too low.
+ if (ticks < MinAllowableTimerPeriod) {
+ //BX_INFO(("register_timer_ticks: adjusting ticks of %llu to min of %u",
+ // ticks, MinAllowableTimerPeriod));
+ ticks = MinAllowableTimerPeriod;
+ }
+
+ for (i=0; i < numTimers; i++) {
+ if (timer[i].inUse == 0)
+ break;
+ }
+
+ timer[i].inUse = 1;
+ timer[i].period = ticks;
+ timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) +
+ ticks;
+ timer[i].active = active;
+ timer[i].continuous = continuous;
+ timer[i].funct = funct;
+ timer[i].this_ptr = this_ptr;
+ strncpy(timer[i].id, id, BxMaxTimerIDLen);
+ timer[i].id[BxMaxTimerIDLen-1] = 0; // Null terminate if not already.
+
+ if (active) {
+ if (ticks < Bit64u(currCountdown)) {
+ // This new timer needs to fire before the current countdown.
+ // Skew the current countdown and countdown period to be smaller
+ // by the delta.
+ currCountdownPeriod -= (currCountdown - Bit32u(ticks));
+ currCountdown = Bit32u(ticks);
+ }
+ }
+
+ // If we didn't find a free slot, increment the bound, numTimers.
+ if (i==numTimers)
+ numTimers++; // One new timer installed.
+
+ // Return timer id.
+ return(i);
+}
+
+
+ void
+bx_pc_system_c::countdownEvent(void)
+{
+ unsigned i;
+ Bit64u minTimeToFire;
+ bx_bool triggered[BX_MAX_TIMERS];
+
+ // The countdown decremented to 0. We need to service all the active
+ // timers, and invoke callbacks from those timers which have fired.
+#if BX_TIMER_DEBUG
+ if (currCountdown != 0)
+ BX_PANIC(("countdownEvent: ticks!=0"));
+#endif
+
+ // Increment global ticks counter by number of ticks which have
+ // elapsed since the last update.
+ ticksTotal += Bit64u(currCountdownPeriod);
+ minTimeToFire = (Bit64u) -1;
+
+ for (i=0; i < numTimers; i++) {
+ triggered[i] = 0; // Reset triggered flag.
+ if (timer[i].active) {
+#if BX_TIMER_DEBUG
+ if (ticksTotal > timer[i].timeToFire)
+ BX_PANIC(("countdownEvent: ticksTotal > timeToFire[%u], D " FMT_LL "u", i,
+ timer[i].timeToFire-ticksTotal));
+#endif
+ if (ticksTotal == timer[i].timeToFire) {
+ // This timer is ready to fire.
+ triggered[i] = 1;
+
+ if (timer[i].continuous==0) {
+ // If triggered timer is one-shot, deactive.
+ timer[i].active = 0;
+ }
+ else {
+ // Continuous timer, increment time-to-fire by period.
+ timer[i].timeToFire += timer[i].period;
+ if (timer[i].timeToFire < minTimeToFire)
+ minTimeToFire = timer[i].timeToFire;
+ }
+ }
+ else {
+ // This timer is not ready to fire yet.
+ if (timer[i].timeToFire < minTimeToFire)
+ minTimeToFire = timer[i].timeToFire;
+ }
+ }
+ }
+
+ // Calculate next countdown period. We need to do this before calling
+ // any of the callbacks, as they may call timer features, which need
+ // to be advanced to the next countdown cycle.
+ currCountdown = currCountdownPeriod =
+ Bit32u(minTimeToFire - ticksTotal);
+
+ for (i=0; i < numTimers; i++) {
+ // Call requested timer function. It may request a different
+ // timer period or deactivate etc.
+ if (triggered[i]) {
+ timer[i].funct(timer[i].this_ptr);
+ }
+ }
+}
+
+ void
+bx_pc_system_c::nullTimer(void* this_ptr)
+{
+ // This function is always inserted in timer[0]. It is sort of
+ // a heartbeat timer. It ensures that at least one timer is
+ // always active to make the timer logic more simple, and has
+ // a duration of less than the maximum 32-bit integer, so that
+ // a 32-bit size can be used for the hot countdown timer. The
+ // rest of the timer info can be 64-bits. This is also a good
+ // place for some logic to report actual emulated
+ // instructions-per-second (IPS) data when measured relative to
+ // the host computer's wall clock.
+
+ UNUSED(this_ptr);
+
+#if SpewPeriodicTimerInfo
+ BX_INFO(("==================================="));
+ for (unsigned i=0; i < bx_pc_system.numTimers; i++) {
+ if (bx_pc_system.timer[i].active) {
+ BX_INFO(("BxTimer(%s): period=" FMT_LL "u, continuous=%u",
+ bx_pc_system.timer[i].id, bx_pc_system.timer[i].period,
+ bx_pc_system.timer[i].continuous));
+ }
+ }
+#endif
+}
+
+#if BX_DEBUGGER
+ void
+bx_pc_system_c::timebp_handler(void* this_ptr)
+{
+ BX_CPU(0)->break_point = BREAK_POINT_TIME;
+ BX_DEBUG(( "Time breakpoint triggered" ));
+
+ if (timebp_queue_size > 1) {
+ Bit64s new_diff = timebp_queue[1] - bx_pc_system.time_ticks();
+ bx_pc_system.activate_timer_ticks(timebp_timer, new_diff, 1);
+ }
+ timebp_queue_size--;
+ for (int i = 0; i < timebp_queue_size; i++)
+ timebp_queue[i] = timebp_queue[i+1];
+}
+#endif // BX_DEBUGGER
+
+ Bit64u
+bx_pc_system_c::time_usec_sequential() {
+ Bit64u this_time_usec = time_usec();
+ if(this_time_usec != lastTimeUsec) {
+ Bit64u diff_usec = this_time_usec-lastTimeUsec;
+ lastTimeUsec = this_time_usec;
+ if(diff_usec >= usecSinceLast) {
+ usecSinceLast = 0;
+ } else {
+ usecSinceLast -= diff_usec;
+ }
+ }
+ usecSinceLast++;
+ return (this_time_usec+usecSinceLast);
+}
+ Bit64u
+bx_pc_system_c::time_usec() {
+ return (Bit64u) (((double)(Bit64s)time_ticks()) / m_ips );
+}
+
+ void
+bx_pc_system_c::start_timers(void)
+{
+}
+
+ void
+bx_pc_system_c::activate_timer_ticks(unsigned i, Bit64u ticks, bx_bool continuous)
+{
+#if BX_TIMER_DEBUG
+ if (i >= numTimers)
+ BX_PANIC(("activate_timer_ticks: timer %u OOB", i));
+ if (timer[i].period < MinAllowableTimerPeriod)
+ BX_PANIC(("activate_timer_ticks: timer[%u].period of " FMT_LL "u < min of %u",
+ i, timer[i].period, MinAllowableTimerPeriod));
+#endif
+
+ // If the timer frequency is rediculously low, make it more sane.
+ // This happens when 'ips' is too low.
+ if (ticks < MinAllowableTimerPeriod) {
+ //BX_INFO(("activate_timer_ticks: adjusting ticks of %llu to min of %u",
+ // ticks, MinAllowableTimerPeriod));
+ ticks = MinAllowableTimerPeriod;
+ }
+
+ timer[i].period = ticks;
+ timer[i].timeToFire = (ticksTotal + Bit64u(currCountdownPeriod-currCountdown)) +
+ ticks;
+ timer[i].active = 1;
+ timer[i].continuous = continuous;
+
+ if (ticks < Bit64u(currCountdown)) {
+ // This new timer needs to fire before the current countdown.
+ // Skew the current countdown and countdown period to be smaller
+ // by the delta.
+ currCountdownPeriod -= (currCountdown - Bit32u(ticks));
+ currCountdown = Bit32u(ticks);
+ }
+}
+
+ void
+bx_pc_system_c::activate_timer(unsigned i, Bit32u useconds, bx_bool continuous)
+{
+ Bit64u ticks;
+
+#if BX_TIMER_DEBUG
+ if (i >= numTimers)
+ BX_PANIC(("activate_timer: timer %u OOB", i));
+#endif
+
+ // if useconds = 0, use default stored in period field
+ // else set new period from useconds
+ if (useconds==0) {
+ ticks = timer[i].period;
+ }
+ else {
+ // convert useconds to number of ticks
+ ticks = (Bit64u) (double(useconds) * m_ips);
+
+ // If the timer frequency is rediculously low, make it more sane.
+ // This happens when 'ips' is too low.
+ if (ticks < MinAllowableTimerPeriod) {
+ //BX_INFO(("activate_timer: adjusting ticks of %llu to min of %u",
+ // ticks, MinAllowableTimerPeriod));
+ ticks = MinAllowableTimerPeriod;
+ }
+
+ timer[i].period = ticks;
+ }
+
+ activate_timer_ticks(i, ticks, continuous);
+}
+
+ void
+bx_pc_system_c::deactivate_timer( unsigned i )
+{
+#if BX_TIMER_DEBUG
+ if (i >= numTimers)
+ BX_PANIC(("deactivate_timer: timer %u OOB", i));
+#endif
+
+ timer[i].active = 0;
+}
+
+ unsigned
+bx_pc_system_c::unregisterTimer(int timerIndex)
+{
+ unsigned i = (unsigned) timerIndex;
+
+#if BX_TIMER_DEBUG
+ if (i >= numTimers)
+ BX_PANIC(("unregisterTimer: timer %u OOB", i));
+ if (i == 0)
+ BX_PANIC(("unregisterTimer: timer 0 is the nullTimer!"));
+ if (timer[i].inUse == 0)
+ BX_PANIC(("unregisterTimer: timer %u is not in-use!", i));
+#endif
+
+ if (timer[i].active) {
+ BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[i].id));
+ return(0); // Fail.
+ }
+
+ // Reset timer fields for good measure.
+ timer[i].inUse = 0; // No longer registered.
+ timer[i].period = BX_MAX_BIT64S; // Max value (invalid)
+ timer[i].timeToFire = BX_MAX_BIT64S; // Max value (invalid)
+ timer[i].continuous = 0;
+ timer[i].funct = NULL;
+ timer[i].this_ptr = NULL;
+ memset(timer[i].id, 0, BxMaxTimerIDLen);
+
+ return(1); // OK
+}
diff --git a/tools/ioemu/iodev/pci.cc b/tools/ioemu/iodev/pci.cc
new file mode 100644
index 0000000000..6dfe5dbbad
--- /dev/null
+++ b/tools/ioemu/iodev/pci.cc
@@ -0,0 +1,467 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pci.cc,v 1.29 2003/07/31 19:51:42 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// i440FX Support - PMC/DBX
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_PCI_SUPPORT
+
+#define LOG_THIS thePciBridge->
+
+bx_pci_c *thePciBridge = NULL;
+
+ int
+libpci_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ thePciBridge = new bx_pci_c ();
+ bx_devices.pluginPciBridge = thePciBridge;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePciBridge, BX_PLUGIN_PCI);
+ return(0); // Success
+}
+
+ void
+libpci_LTX_plugin_fini(void)
+{
+}
+
+bx_pci_c::bx_pci_c(void)
+{
+ put("PCI");
+ settype(PCILOG);
+}
+
+bx_pci_c::~bx_pci_c(void)
+{
+ // nothing for now
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_pci_c::init(void)
+{
+ // called once when bochs initializes
+ unsigned i;
+ BX_PCI_THIS num_pci_handles = 0;
+
+ /* set unused elements to appropriate values */
+ for (i=0; i < BX_MAX_PCI_DEVICES; i++) {
+ BX_PCI_THIS pci_handler[i].read = NULL;
+ BX_PCI_THIS pci_handler[i].write = NULL;
+ }
+
+ for (i=0; i < 0x100; i++) {
+ BX_PCI_THIS pci_handler_id[i] = BX_MAX_PCI_DEVICES; // not assigned
+ }
+
+ // confAddr accepts dword i/o only
+ DEV_register_ioread_handler(this, read_handler, 0x0CF8, "i440FX", 4);
+ DEV_register_iowrite_handler(this, write_handler, 0x0CF8, "i440FX", 4);
+
+ for (i=0x0CFC; i<=0x0CFF; i++) {
+ DEV_register_ioread_handler(this, read_handler, i, "i440FX", 7);
+ }
+ for (i=0x0CFC; i<=0x0CFF; i++) {
+ DEV_register_iowrite_handler(this, write_handler, i, "i440FX", 7);
+ }
+
+ DEV_register_pci_handlers(this, pci_read_handler, pci_write_handler,
+ BX_PCI_DEVICE(0,0), "440FX Host bridge");
+
+ for (i=0; i<256; i++)
+ BX_PCI_THIS s.i440fx.pci_conf[i] = 0x0;
+ // readonly registers
+ BX_PCI_THIS s.i440fx.pci_conf[0x00] = 0x86;
+ BX_PCI_THIS s.i440fx.pci_conf[0x01] = 0x80;
+ BX_PCI_THIS s.i440fx.pci_conf[0x02] = 0x37;
+ BX_PCI_THIS s.i440fx.pci_conf[0x03] = 0x12;
+ BX_PCI_THIS s.i440fx.pci_conf[0x0b] = 0x06;
+}
+
+ void
+bx_pci_c::reset(unsigned type)
+{
+ BX_PCI_THIS s.i440fx.confAddr = 0;
+ BX_PCI_THIS s.i440fx.confData = 0;
+
+ BX_PCI_THIS s.i440fx.pci_conf[0x04] = 0x06;
+ BX_PCI_THIS s.i440fx.pci_conf[0x05] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x06] = 0x80;
+ BX_PCI_THIS s.i440fx.pci_conf[0x07] = 0x02;
+ BX_PCI_THIS s.i440fx.pci_conf[0x0d] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x0f] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x50] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x51] = 0x01;
+ BX_PCI_THIS s.i440fx.pci_conf[0x52] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x53] = 0x80;
+ BX_PCI_THIS s.i440fx.pci_conf[0x54] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x55] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x56] = 0x00;
+ BX_PCI_THIS s.i440fx.pci_conf[0x57] = 0x01;
+ BX_PCI_THIS s.i440fx.pci_conf[0x58] = 0x10;
+ for (unsigned i=0x59; i<0x60; i++)
+ BX_PCI_THIS s.i440fx.pci_conf[i] = 0x00;
+}
+
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pci_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_PCI_SMF
+ bx_pci_c *class_ptr = (bx_pci_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_pci_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCI_SMF
+
+ switch (address) {
+ case 0x0CF8:
+ {
+ return BX_PCI_THIS s.i440fx.confAddr;
+ }
+ break;
+ case 0x0CFC:
+ case 0x0CFD:
+ case 0x0CFE:
+ case 0x0CFF:
+ {
+ Bit32u handle, retval;
+ Bit8u devfunc, regnum;
+
+ if ((BX_PCI_THIS s.i440fx.confAddr & 0x80FF0000) == 0x80000000) {
+ devfunc = (BX_PCI_THIS s.i440fx.confAddr >> 8) & 0xff;
+ regnum = (BX_PCI_THIS s.i440fx.confAddr & 0xfc) + (address & 0x03);
+ handle = BX_PCI_THIS pci_handler_id[devfunc];
+ if ((io_len <= 4) && (handle < BX_MAX_PCI_DEVICES))
+ retval = (* BX_PCI_THIS pci_handler[handle].read)
+ (BX_PCI_THIS pci_handler[handle].this_ptr, regnum, io_len);
+ else
+ retval = 0xFFFFFFFF;
+ }
+ else
+ retval = 0xFFFFFFFF;
+ BX_PCI_THIS s.i440fx.confData = retval;
+ return retval;
+ }
+ }
+
+ BX_PANIC(("unsupported IO read to port 0x%x",
+ (unsigned) address));
+ return(0xffffffff);
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pci_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PCI_SMF
+ bx_pci_c *class_ptr = (bx_pci_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_pci_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCI_SMF
+
+ switch (address) {
+ case 0xCF8:
+ {
+ BX_PCI_THIS s.i440fx.confAddr = value;
+ if ((value & 0x80FFFF00) == 0x80000000) {
+ BX_DEBUG(("440FX PMC register 0x%02x selected", value & 0xfc));
+ } else if ((value & 0x80000000) == 0x80000000) {
+ BX_DEBUG(("440FX request for bus 0x%02x device 0x%02x function 0x%02x",
+ (value >> 16) & 0xFF, (value >> 11) & 0x1F, (value >> 8) & 0x07));
+ }
+ }
+ break;
+
+ case 0xCFC:
+ case 0xCFD:
+ case 0xCFE:
+ case 0xCFF:
+ {
+ Bit32u handle;
+ Bit8u devfunc, regnum;
+
+ if ((BX_PCI_THIS s.i440fx.confAddr & 0x80FF0000) == 0x80000000) {
+ devfunc = (BX_PCI_THIS s.i440fx.confAddr >> 8) & 0xff;
+ regnum = (BX_PCI_THIS s.i440fx.confAddr & 0xfc) + (address & 0x03);
+ handle = BX_PCI_THIS pci_handler_id[devfunc];
+ if ((io_len <= 4) && (handle < BX_MAX_PCI_DEVICES)) {
+ if (((regnum>=4) && (regnum<=7)) || (regnum==12) || (regnum==13) || (regnum>14)) {
+ (* BX_PCI_THIS pci_handler[handle].write)
+ (BX_PCI_THIS pci_handler[handle].this_ptr, regnum, value, io_len);
+ BX_PCI_THIS s.i440fx.confData = value << (8 * (address & 0x03));
+ }
+ else
+ BX_DEBUG(("read only register, write ignored"));
+ }
+ }
+ }
+ break;
+
+ default:
+ BX_PANIC(("IO write to port 0x%x", (unsigned) address));
+ }
+}
+
+
+ // static pci configuration space read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pci_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len)
+{
+#if !BX_USE_PCI_SMF
+ bx_pci_c *class_ptr = (bx_pci_c *) this_ptr;
+
+ return( class_ptr->pci_read(address, io_len) );
+}
+
+
+ Bit32u
+bx_pci_c::pci_read(Bit8u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCI_SMF
+
+ Bit32u val440fx = 0;
+
+ if (io_len <= 4) {
+ for (unsigned i=0; i<io_len; i++) {
+ val440fx |= (BX_PCI_THIS s.i440fx.pci_conf[address+i] << (i*8));
+ }
+ BX_DEBUG(("440FX PMC read register 0x%02x value 0x%08x", address, val440fx));
+ return val440fx;
+ }
+ else
+ return(0xffffffff);
+}
+
+
+ // static pci configuration space write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pci_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PCI_SMF
+ bx_pci_c *class_ptr = (bx_pci_c *) this_ptr;
+
+ class_ptr->pci_write(address, value, io_len);
+}
+
+ void
+bx_pci_c::pci_write(Bit8u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCI_SMF
+
+ Bit8u value8;
+
+ if (io_len <= 4) {
+ for (unsigned i=0; i<io_len; i++) {
+ value8 = (value >> (i*8)) & 0xFF;
+ switch (address+i) {
+ case 0x06:
+ case 0x0c:
+ break;
+ default:
+ BX_PCI_THIS s.i440fx.pci_conf[address+i] = value8;
+ BX_DEBUG(("440FX PMC write register 0x%02x value 0x%02x", address,
+ value8));
+ }
+ }
+ }
+}
+
+
+ Bit8u
+bx_pci_c::rd_memType (Bit32u addr)
+{
+ switch ((addr & 0xFC000) >> 12) {
+ case 0xC0:
+ return (BX_PCI_THIS s.i440fx.pci_conf[0x5A] & 0x1);
+ case 0xC4:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5A] >> 4) & 0x1);
+ case 0xC8:
+ return (BX_PCI_THIS s.i440fx.pci_conf[0x5B] & 0x1);
+ case 0xCC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5B] >> 4) & 0x1);
+
+
+ case 0xD0:
+ return (BX_PCI_THIS s.i440fx.pci_conf[0x5C] & 0x1);
+ case 0xD4:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5C] >> 4) & 0x1);
+ case 0xD8:
+ return (BX_PCI_THIS s.i440fx.pci_conf[0x5D] & 0x1);
+ case 0xDC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5D] >> 4) & 0x1);
+
+ case 0xE0:
+ return (BX_PCI_THIS s.i440fx.pci_conf[0x5E] & 0x1);
+ case 0xE4:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5E] >> 4) & 0x1);
+ case 0xE8:
+ return (BX_PCI_THIS s.i440fx.pci_conf[0x5F] & 0x1);
+ case 0xEC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5F] >> 4) & 0x1);
+
+ case 0xF0:
+ case 0xF4:
+ case 0xF8:
+ case 0xFC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x59] >> 4) & 0x1);
+
+ default:
+ BX_PANIC(("rd_memType () Error: Memory Type not known !"));
+ return(0); // keep compiler happy
+ break;
+ }
+
+}
+
+ Bit8u
+bx_pci_c::wr_memType (Bit32u addr)
+{
+ switch ((addr & 0xFC000) >> 12) {
+ case 0xC0:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5A] >> 1) & 0x1);
+ case 0xC4:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5A] >> 5) & 0x1);
+ case 0xC8:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5B] >> 1) & 0x1);
+ case 0xCC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5B] >> 5) & 0x1);
+
+
+ case 0xD0:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5C] >> 1) & 0x1);
+ case 0xD4:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5C] >> 5) & 0x1);
+ case 0xD8:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5D] >> 1) & 0x1);
+ case 0xDC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5D] >> 5) & 0x1);
+
+ case 0xE0:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5E] >> 1) & 0x1);
+ case 0xE4:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5E] >> 5) & 0x1);
+ case 0xE8:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5F] >> 1) & 0x1);
+ case 0xEC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x5F] >> 5) & 0x1);
+
+ case 0xF0:
+ case 0xF4:
+ case 0xF8:
+ case 0xFC:
+ return ( (BX_PCI_THIS s.i440fx.pci_conf[0x59] >> 5) & 0x1);
+
+ default:
+ BX_PANIC(("wr_memType () Error: Memory Type not known !"));
+ return(0); // keep compiler happy
+ break;
+ }
+}
+
+ void
+bx_pci_c::print_i440fx_state()
+{
+ int i;
+
+ BX_DEBUG(( "i440fxConfAddr:0x%08x", BX_PCI_THIS s.i440fx.confAddr ));
+ BX_DEBUG(( "i440fxConfData:0x%08x", BX_PCI_THIS s.i440fx.confData ));
+
+#ifdef DUMP_FULL_I440FX
+ for (i=0; i<256; i++) {
+ BX_DEBUG(( "i440fxArray%02x:0x%02x", i, BX_PCI_THIS s.i440fx.pci_conf[i] ));
+ }
+#else /* DUMP_FULL_I440FX */
+ for (i=0x59; i<0x60; i++) {
+ BX_DEBUG(( "i440fxArray%02x:0x%02x", i, BX_PCI_THIS s.i440fx.pci_conf[i] ));
+ }
+#endif /* DUMP_FULL_I440FX */
+}
+
+ bx_bool
+bx_pci_c::register_pci_handlers( void *this_ptr, bx_pci_read_handler_t f1,
+ bx_pci_write_handler_t f2, Bit8u devfunc,
+ const char *name)
+{
+ unsigned handle;
+
+ /* first check if device/function is available */
+ if (BX_PCI_THIS pci_handler_id[devfunc] == BX_MAX_PCI_DEVICES) {
+ if (BX_PCI_THIS num_pci_handles >= BX_MAX_PCI_DEVICES) {
+ BX_INFO(("too many PCI devices installed."));
+ BX_PANIC((" try increasing BX_MAX_PCI_DEVICES"));
+ return false;
+ }
+ handle = BX_PCI_THIS num_pci_handles++;
+ BX_PCI_THIS pci_handler[handle].read = f1;
+ BX_PCI_THIS pci_handler[handle].write = f2;
+ BX_PCI_THIS pci_handler[handle].this_ptr = this_ptr;
+ BX_PCI_THIS pci_handler_id[devfunc] = handle;
+ BX_INFO(("%s present at device %d, function %d", name, devfunc >> 3,
+ devfunc & 0x07));
+ return true; // device/function mapped successfully
+ }
+ else {
+ return false; // device/function not available, return false.
+ }
+}
+#endif /* BX_PCI_SUPPORT */
diff --git a/tools/ioemu/iodev/pci.h b/tools/ioemu/iodev/pci.h
new file mode 100644
index 0000000000..92e7beef4f
--- /dev/null
+++ b/tools/ioemu/iodev/pci.h
@@ -0,0 +1,90 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pci.h,v 1.14 2003/01/23 19:31:27 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#define BX_MAX_PCI_DEVICES 20
+
+#define BX_PCI_DEVICE(device, function) ((device)<<3 | (function))
+
+typedef Bit32u (*bx_pci_read_handler_t)(void *, Bit8u, unsigned);
+typedef void (*bx_pci_write_handler_t)(void *, Bit8u, Bit32u, unsigned);
+
+#if BX_USE_PCI_SMF
+# define BX_PCI_SMF static
+# define BX_PCI_THIS thePciBridge->
+#else
+# define BX_PCI_SMF
+# define BX_PCI_THIS this->
+#endif
+
+
+typedef struct {
+ Bit32u confAddr;
+ Bit32u confData;
+ Bit8u pci_conf[256];
+ } bx_def440fx_t;
+
+
+
+class bx_pci_c : public bx_pci_stub_c {
+
+public:
+ bx_pci_c(void);
+ ~bx_pci_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+ virtual bx_bool register_pci_handlers(void *this_ptr,
+ bx_pci_read_handler_t f1,
+ bx_pci_write_handler_t f2,
+ Bit8u devfunc, const char *name);
+ virtual void print_i440fx_state(void);
+ virtual Bit8u rd_memType (Bit32u addr);
+ virtual Bit8u wr_memType (Bit32u addr);
+
+private:
+ Bit8u pci_handler_id[0x100]; // 256 devices/functions
+ struct {
+ bx_pci_read_handler_t read;
+ bx_pci_write_handler_t write;
+ void *this_ptr;
+ } pci_handler[BX_MAX_PCI_DEVICES];
+ unsigned num_pci_handles;
+
+ struct {
+ bx_def440fx_t i440fx;
+ } s;
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+ static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len);
+ static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len);
+#if !BX_USE_PCI_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+ Bit32u pci_read(Bit8u address, unsigned io_len);
+ void pci_write(Bit8u address, Bit32u value, unsigned io_len);
+#endif
+ };
diff --git a/tools/ioemu/iodev/pci2isa.cc b/tools/ioemu/iodev/pci2isa.cc
new file mode 100644
index 0000000000..54c3bc441a
--- /dev/null
+++ b/tools/ioemu/iodev/pci2isa.cc
@@ -0,0 +1,294 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pci2isa.cc,v 1.10 2003/07/31 19:51:42 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// i440FX Support - PCI-to-ISA bridge (PIIX3)
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_PCI_SUPPORT
+
+#define LOG_THIS thePci2IsaBridge->
+
+bx_pci2isa_c *thePci2IsaBridge = NULL;
+
+ int
+libpci2isa_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ thePci2IsaBridge = new bx_pci2isa_c ();
+ bx_devices.pluginPci2IsaBridge = thePci2IsaBridge;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePci2IsaBridge, BX_PLUGIN_PCI2ISA);
+ return(0); // Success
+}
+
+ void
+libpci2isa_LTX_plugin_fini(void)
+{
+}
+
+bx_pci2isa_c::bx_pci2isa_c(void)
+{
+ put("P2I");
+ settype(PCI2ISALOG);
+}
+
+bx_pci2isa_c::~bx_pci2isa_c(void)
+{
+ // nothing for now
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_pci2isa_c::init(void)
+{
+ // called once when bochs initializes
+
+ DEV_register_pci_handlers(this, pci_read_handler, pci_write_handler,
+ BX_PCI_DEVICE(1,0), "PIIX3 PCI-to-ISA bridge");
+
+ DEV_register_iowrite_handler(this, write_handler, 0x00B2, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x00B3, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x04D0, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x04D1, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0CF9, "PIIX3 PCI-to-ISA bridge", 1);
+
+ DEV_register_ioread_handler(this, read_handler, 0x00B2, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x00B3, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x04D0, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x04D1, "PIIX3 PCI-to-ISA bridge", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0CF9, "PIIX3 PCI-to-ISA bridge", 1);
+
+ for (unsigned i=0; i<256; i++)
+ BX_P2I_THIS s.pci_conf[i] = 0x0;
+ // readonly registers
+ BX_P2I_THIS s.pci_conf[0x00] = 0x86;
+ BX_P2I_THIS s.pci_conf[0x01] = 0x80;
+ BX_P2I_THIS s.pci_conf[0x02] = 0x00;
+ BX_P2I_THIS s.pci_conf[0x03] = 0x70;
+ BX_P2I_THIS s.pci_conf[0x0a] = 0x01;
+ BX_P2I_THIS s.pci_conf[0x0b] = 0x06;
+ BX_P2I_THIS s.pci_conf[0x0e] = 0x80;
+}
+
+ void
+bx_pci2isa_c::reset(unsigned type)
+{
+ BX_P2I_THIS s.pci_conf[0x04] = 0x07;
+ BX_P2I_THIS s.pci_conf[0x05] = 0x00;
+ BX_P2I_THIS s.pci_conf[0x06] = 0x00;
+ BX_P2I_THIS s.pci_conf[0x07] = 0x02;
+ BX_P2I_THIS s.pci_conf[0x4c] = 0x4d;
+ BX_P2I_THIS s.pci_conf[0x4e] = 0x03;
+ BX_P2I_THIS s.pci_conf[0x4f] = 0x00;
+ BX_P2I_THIS s.pci_conf[0x60] = 0x80;
+ BX_P2I_THIS s.pci_conf[0x69] = 0x02;
+ BX_P2I_THIS s.pci_conf[0x70] = 0x80;
+ BX_P2I_THIS s.pci_conf[0x76] = 0x0c;
+ BX_P2I_THIS s.pci_conf[0x77] = 0x0c;
+ BX_P2I_THIS s.pci_conf[0x78] = 0x02;
+ BX_P2I_THIS s.pci_conf[0x79] = 0x00;
+ BX_P2I_THIS s.pci_conf[0x80] = 0x00;
+ BX_P2I_THIS s.pci_conf[0x82] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xa0] = 0x08;
+ BX_P2I_THIS s.pci_conf[0xa0] = 0x08;
+ BX_P2I_THIS s.pci_conf[0xa2] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xa3] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xa4] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xa5] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xa6] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xa7] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xa8] = 0x0f;
+ BX_P2I_THIS s.pci_conf[0xaa] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xab] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xac] = 0x00;
+ BX_P2I_THIS s.pci_conf[0xae] = 0x00;
+
+ BX_P2I_THIS s.elcr1 = 0x00;
+ BX_P2I_THIS s.elcr2 = 0x00;
+}
+
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pci2isa_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_P2I_SMF
+ bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_pci2isa_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_P2I_SMF
+
+ switch (address) {
+ case 0x00b2:
+ BX_ERROR(("read: APM command register not supported yet"));
+ break;
+ case 0x00b3:
+ BX_ERROR(("read: APM status register not supported yet"));
+ break;
+ case 0x04d0:
+ return(BX_P2I_THIS s.elcr1);
+ break;
+ case 0x04d1:
+ return(BX_P2I_THIS s.elcr2);
+ break;
+ case 0x0cf9:
+ BX_ERROR(("read: CPU reset register not supported yet"));
+ break;
+ }
+
+ return(0xffffffff);
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pci2isa_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_P2I_SMF
+ bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_pci2isa_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_P2I_SMF
+
+ switch (address) {
+ case 0x00b2:
+ BX_ERROR(("write: APM command register not supported yet"));
+ break;
+ case 0x00b3:
+ BX_ERROR(("write: APM status register not supported yet"));
+ break;
+ case 0x04d0:
+ BX_P2I_THIS s.elcr1 = (value & 0xf8);
+ BX_ERROR(("write: ELCR1 changes have no effect yet"));
+ break;
+ case 0x04d1:
+ BX_P2I_THIS s.elcr2 = (value & 0xde);
+ BX_ERROR(("write: ELCR2 changes have no effect yet"));
+ break;
+ case 0x0cf9:
+ BX_ERROR(("write: CPU reset register not supported yet"));
+ break;
+ }
+}
+
+
+ // static pci configuration space read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pci2isa_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len)
+{
+#if !BX_USE_P2I_SMF
+ bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr;
+
+ return( class_ptr->pci_read(address, io_len) );
+}
+
+
+ Bit32u
+bx_pci2isa_c::pci_read(Bit8u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_P2I_SMF
+
+ Bit32u value = 0;
+
+ if (io_len <= 4) {
+ for (unsigned i=0; i<io_len; i++) {
+ value |= (BX_P2I_THIS s.pci_conf[address+i] << (i*8));
+ }
+ BX_DEBUG(("PIIX3 PCI-to-ISA read register 0x%02x value 0x%08x", address, value));
+ return value;
+ }
+ else
+ return(0xffffffff);
+}
+
+
+ // static pci configuration space write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pci2isa_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_P2I_SMF
+ bx_pci2isa_c *class_ptr = (bx_pci2isa_c *) this_ptr;
+
+ class_ptr->pci_write(address, value, io_len);
+}
+
+ void
+bx_pci2isa_c::pci_write(Bit8u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_P2I_SMF
+
+ Bit8u value8;
+
+ if (io_len <= 4) {
+ for (unsigned i=0; i<io_len; i++) {
+ value8 = (value >> (i*8)) & 0xFF;
+ switch (address+i) {
+ case 0x06:
+ break;
+ default:
+ BX_P2I_THIS s.pci_conf[address+i] = value8;
+ BX_DEBUG(("PIIX3 PCI-to-ISA write register 0x%02x value 0x%02x", address,
+ value8));
+ }
+ }
+ }
+}
+
+#endif /* BX_PCI_SUPPORT */
diff --git a/tools/ioemu/iodev/pci2isa.h b/tools/ioemu/iodev/pci2isa.h
new file mode 100644
index 0000000000..1517052d92
--- /dev/null
+++ b/tools/ioemu/iodev/pci2isa.h
@@ -0,0 +1,63 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pci2isa.h,v 1.4 2002/11/09 20:51:40 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#if BX_USE_P2I_SMF
+# define BX_P2I_SMF static
+# define BX_P2I_THIS thePci2IsaBridge->
+#else
+# define BX_P2I_SMF
+# define BX_P2I_THIS this->
+#endif
+
+
+class bx_pci2isa_c : public bx_devmodel_c {
+
+public:
+ bx_pci2isa_c(void);
+ ~bx_pci2isa_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+
+private:
+
+ struct {
+ Bit8u pci_conf[256];
+ Bit8u elcr1;
+ Bit8u elcr2;
+ } s;
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+ static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len);
+ static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len);
+#if !BX_USE_P2I_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+ Bit32u pci_read(Bit8u address, unsigned io_len);
+ void pci_write(Bit8u address, Bit32u value, unsigned io_len);
+#endif
+ };
diff --git a/tools/ioemu/iodev/pciusb.cc b/tools/ioemu/iodev/pciusb.cc
new file mode 100644
index 0000000000..e2d4248369
--- /dev/null
+++ b/tools/ioemu/iodev/pciusb.cc
@@ -0,0 +1,668 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pciusb.cc,v 1.3 2003/02/06 19:09:24 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2003 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// Experimental PCI USB adapter
+// Benjamin D Lunt (fys@cybertrails.com) coded most of this usb emulation.
+// I hope to add to this code to make it more functionable.
+//
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_PCI_SUPPORT && BX_PCI_USB_SUPPORT
+
+#define LOG_THIS theUSBDevice->
+
+bx_pciusb_c* theUSBDevice = NULL;
+
+ int
+libpciusb_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theUSBDevice = new bx_pciusb_c ();
+ bx_devices.pluginPciUSBAdapter = theUSBDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUSBDevice, BX_PLUGIN_PCIUSB);
+ return 0; // Success
+}
+
+ void
+libpciusb_LTX_plugin_fini(void)
+{
+}
+
+
+bx_pciusb_c::bx_pciusb_c(void)
+{
+ put("USB");
+ settype(PCIUSBLOG);
+}
+
+bx_pciusb_c::~bx_pciusb_c(void)
+{
+ // nothing for now
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_pciusb_c::init(void)
+{
+ // called once when bochs initializes
+
+ if (!bx_options.usb[0].Oenabled->get()) return;
+
+ Bit16u base_ioaddr = bx_options.usb[0].Oioaddr->get();
+ Bit8u irq = bx_options.usb[0].Oirq->get();
+
+ DEV_register_irq(irq, "USB Hub #1");
+ BX_USB_THIS hub[0].irq = irq;
+
+ // Call our timer routine every 1mS (1,000uS)
+ // Continuous and active
+ BX_USB_THIS hub[0].timer_index =
+ bx_pc_system.register_timer(this, usb_timer_handler, 1000, 1,1, "usb.timer");
+
+ for (unsigned addr=base_ioaddr; addr<(unsigned)(base_ioaddr+0x14); addr++) {
+ BX_DEBUG(("register read/write: 0x%04x", addr));
+ DEV_register_ioread_handler(this, read_handler, addr, "USB Hub #1", 7);
+ DEV_register_iowrite_handler(this, write_handler, addr, "USB Hub #1", 7);
+ }
+ BX_USB_THIS hub[0].base_ioaddr = base_ioaddr;
+
+ DEV_register_pci_handlers(this,
+ pci_read_handler,
+ pci_write_handler,
+ BX_PCI_DEVICE(1,2),
+ "Experimental PCI USB");
+
+ for (unsigned i=0; i<256; i++) {
+ BX_USB_THIS hub[0].pci_conf[i] = 0x0;
+ }
+
+ BX_INFO(("usb1 at 0x%04x-0x%04x irq %d", base_ioaddr, base_ioaddr+0x13, irq));
+}
+
+ void
+bx_pciusb_c::reset(unsigned type)
+{
+ unsigned i;
+
+ static const struct reset_vals_t {
+ unsigned addr;
+ unsigned char val;
+ } reset_vals[] = {
+ { 0x00, 0x86 }, { 0x01, 0x80 }, // 0x8086 = vendor
+ { 0x02, 0x20 }, { 0x03, 0x70 }, // 0x7020 = device
+ { 0x04, 0x05 }, { 0x05, 0x00 }, // command_io
+ { 0x06, 0x80 }, { 0x07, 0x02 }, // status
+ { 0x08, 0x01 }, // revision number
+ { 0x09, 0x00 }, // interface
+ { 0x0a, 0x03 }, // class_sub USB Host Controller
+ { 0x0b, 0x0c }, // class_base Serial Bus Controller
+ { 0x0D, 0x20 }, // bus latency
+ { 0x0e, 0x00 }, // header_type_generic
+ // address space 0x20 - 0x23
+ { 0x20, ((bx_options.usb[0].Oioaddr->get() & 0xE0) | 0x01) },
+ { 0x21, (bx_options.usb[0].Oioaddr->get() >> 8) },
+ { 0x22, 0x00 }, { 0x23, 0x00 },
+ { 0x3c, bx_options.usb[0].Oirq->get() }, // IRQ
+ { 0x3d, 0x04 }, // INT
+ { 0x6a, 0x01 }, // USB clock
+ { 0xc1, 0x20 } // PIRQ enable
+
+ };
+ for (i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) {
+ BX_USB_THIS hub[0].pci_conf[reset_vals[i].addr] = reset_vals[i].val;
+ }
+
+ // reset locals
+ BX_USB_THIS global_reset = 0;
+
+ // Put the USB registers into their RESET state
+ for (i=0; i<BX_USB_CONFDEV; i++) {
+ BX_USB_THIS hub[i].usb_command.max_packet_size = 0;
+ BX_USB_THIS hub[i].usb_command.configured = 0;
+ BX_USB_THIS hub[i].usb_command.debug = 0;
+ BX_USB_THIS hub[i].usb_command.resume = 0;
+ BX_USB_THIS hub[i].usb_command.suspend = 1;
+ BX_USB_THIS hub[i].usb_command.host_reset = 0;
+ BX_USB_THIS hub[i].usb_command.reset = 0;
+ BX_USB_THIS hub[i].usb_command.schedule = 0;
+ BX_USB_THIS hub[i].usb_status.error_interrupt = 0;
+ BX_USB_THIS hub[i].usb_status.host_error = 0;
+ BX_USB_THIS hub[i].usb_status.host_halted = 0;
+ BX_USB_THIS hub[i].usb_status.interrupt = 0;
+ BX_USB_THIS hub[i].usb_status.pci_error = 0;
+ BX_USB_THIS hub[i].usb_status.resume = 0;
+ BX_USB_THIS hub[i].usb_enable.short_packet = 0;
+ BX_USB_THIS hub[i].usb_enable.on_complete = 0;
+ BX_USB_THIS hub[i].usb_enable.resume = 0;
+ BX_USB_THIS hub[i].usb_enable.timeout_crc = 0;
+ BX_USB_THIS hub[i].usb_frame_num.frame_num = 0x0000;
+ BX_USB_THIS hub[i].usb_frame_base.frame_base = 0x00000000;
+ BX_USB_THIS hub[i].usb_sof.sof_timing = 0x40;
+ for (unsigned j=0; j<USB_NUM_PORTS; j++) {
+ BX_USB_THIS hub[i].usb_port[j].connect_changed = 0;
+ BX_USB_THIS hub[i].usb_port[j].line_dminus = 0;
+ BX_USB_THIS hub[i].usb_port[j].line_dplus = 0;
+ BX_USB_THIS hub[i].usb_port[j].low_speed = 0;
+ BX_USB_THIS hub[i].usb_port[j].reset = 0;
+ BX_USB_THIS hub[i].usb_port[j].resume = 0;
+ BX_USB_THIS hub[i].usb_port[j].suspend = 0;
+ BX_USB_THIS hub[i].usb_port[j].enabled = 0;
+ BX_USB_THIS hub[i].usb_port[j].able_changed = 0;
+ BX_USB_THIS hub[i].usb_port[j].status = 0;
+ }
+ }
+}
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pciusb_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_PCIUSB_SMF
+ bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_pciusb_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCIUSB_SMF
+ Bit32u val = 0x0;
+ Bit8u offset,port;
+
+ BX_DEBUG(("register read from address 0x%04x - ", (unsigned) address));
+
+ offset = address - BX_USB_THIS hub[0].base_ioaddr;
+
+ switch (offset) {
+ case 0x0C: // Start of Frame Modify
+ case 0x11: // port0 (high byte read)
+ case 0x13: // port1 (high byte read)
+ if (io_len != 1)
+ BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
+ break;
+ case 0x10: // port0
+ case 0x12: // port1
+ if ((io_len < 1) || (io_len > 2))
+ BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
+ break;
+ case 0x00: // command register (16-bit)
+ case 0x02: // status register (16-bit)
+ case 0x04: // interrupt enable register (1-bit)
+ case 0x06: // frame number register (16-bit)
+ if (io_len != 2)
+ BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
+ break;
+ case 0x08: // frame base register (32-bit)
+ if (io_len != 4)
+ BX_PANIC(("io read from port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
+ break;
+ }
+
+ switch (offset) {
+ case 0x00: // command register (16-bit)
+ val = BX_USB_THIS hub[0].usb_command.max_packet_size << 7
+ | BX_USB_THIS hub[0].usb_command.configured << 6
+ | BX_USB_THIS hub[0].usb_command.debug << 5
+ | BX_USB_THIS hub[0].usb_command.resume << 4
+ | BX_USB_THIS hub[0].usb_command.suspend << 3
+ | BX_USB_THIS hub[0].usb_command.reset << 2
+ | BX_USB_THIS hub[0].usb_command.host_reset << 1
+ | BX_USB_THIS hub[0].usb_command.schedule;
+ break;
+
+ case 0x02: // status register (16-bit)
+ val = BX_USB_THIS hub[0].usb_status.host_halted << 5
+ | BX_USB_THIS hub[0].usb_status.host_error << 4
+ | BX_USB_THIS hub[0].usb_status.pci_error << 3
+ | BX_USB_THIS hub[0].usb_status.resume << 2
+ | BX_USB_THIS hub[0].usb_status.error_interrupt << 1
+ | BX_USB_THIS hub[0].usb_status.interrupt;
+ break;
+
+ case 0x04: // interrupt enable register (16-bit)
+ val = BX_USB_THIS hub[0].usb_enable.short_packet << 3
+ | BX_USB_THIS hub[0].usb_enable.on_complete << 2
+ | BX_USB_THIS hub[0].usb_enable.resume << 1
+ | BX_USB_THIS hub[0].usb_enable.timeout_crc;
+ break;
+
+ case 0x06: // frame number register (16-bit)
+ val = BX_USB_THIS hub[0].usb_frame_num.frame_num;
+ break;
+
+ case 0x08: // frame base register (32-bit)
+ val = BX_USB_THIS hub[0].usb_frame_base.frame_base;
+ break;
+
+ case 0x0C: // start of Frame Modify register (8-bit)
+ val = BX_USB_THIS hub[0].usb_sof.sof_timing;
+ break;
+
+ case 0x10: // port0
+ case 0x12: // port1
+ port = (offset & 0x0F) >> 1;
+ if (port < USB_NUM_PORTS) {
+ val = BX_USB_THIS hub[0].usb_port[port].suspend << 12
+ | BX_USB_THIS hub[0].usb_port[port].reset << 9
+ | BX_USB_THIS hub[0].usb_port[port].low_speed << 8
+ | 1 << 7
+ | BX_USB_THIS hub[0].usb_port[port].resume << 6
+ | BX_USB_THIS hub[0].usb_port[port].line_dplus << 5
+ | BX_USB_THIS hub[0].usb_port[port].line_dminus << 4
+ | BX_USB_THIS hub[0].usb_port[port].able_changed << 3
+ | BX_USB_THIS hub[0].usb_port[port].enabled << 2
+ | BX_USB_THIS hub[0].usb_port[port].connect_changed << 1
+ | BX_USB_THIS hub[0].usb_port[port].status;
+ break;
+ } // else fall through to default
+
+ default:
+ val = 0; // keep compiler happy
+ BX_PANIC(("unsupported io read from address=0x%04x!", (unsigned) address));
+ break;
+ }
+
+ BX_DEBUG(("val = 0x%08x", (Bit32u) val));
+
+ return(val);
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pciusb_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PCIUSB_SMF
+ bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_pciusb_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCIUSB_SMF
+ Bit8u offset,port;
+
+ BX_DEBUG(("register write to address 0x%04x - ", (unsigned) address));
+
+ offset = address - BX_USB_THIS hub[0].base_ioaddr;
+
+ switch (offset) {
+ case 0x0C: // Start of Frame Modify
+ if (io_len != 1)
+ BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
+ break;
+ case 0x00: // command register (16-bit)
+ case 0x02: // status register (16-bit)
+ case 0x04: // interrupt enable register (1-bit)
+ case 0x06: // frame number register (16-bit)
+ case 0x10: // port0
+ case 0x12: // port1
+ if (io_len != 2)
+ BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
+ break;
+ case 0x08: // frame base register (32-bit)
+ if (io_len != 4)
+ BX_PANIC(("io write to port 0x%04x, bad len=%u", (unsigned) address, (unsigned) io_len));
+ break;
+ }
+
+ switch (offset) {
+ case 0x00: // command register (16-bit) (R/W)
+ if (value & 0xFF00)
+ BX_ERROR(("write to command register with bits 15:8 not zero: 0x%04x", value));
+
+ BX_USB_THIS hub[0].usb_command.max_packet_size = (value & 0x80) ? 1: 0;
+ BX_USB_THIS hub[0].usb_command.configured = (value & 0x40) ? 1: 0;
+ BX_USB_THIS hub[0].usb_command.debug = (value & 0x20) ? 1: 0;
+ BX_USB_THIS hub[0].usb_command.resume = (value & 0x10) ? 1: 0;
+ BX_USB_THIS hub[0].usb_command.suspend = (value & 0x08) ? 1: 0;
+ BX_USB_THIS hub[0].usb_command.reset = (value & 0x04) ? 1: 0;
+ BX_USB_THIS hub[0].usb_command.host_reset = (value & 0x02) ? 1: 0;
+ BX_USB_THIS hub[0].usb_command.schedule = (value & 0x01) ? 1: 0;
+
+ // If software set the reset bit, we need to set reset bit of each port for 10ms.
+ if (BX_USB_THIS hub[0].usb_command.reset)
+ BX_USB_THIS global_reset = 10;
+
+ // If host_reset then reset all registers, etc.
+ if (BX_USB_THIS hub[0].usb_command.host_reset)
+ BX_USB_THIS reset(0);
+
+ // If Run/Stop, identify in log and ignore
+ if (BX_USB_THIS hub[0].usb_command.schedule)
+ BX_INFO(("Software set Schedule bit in Command register"));
+
+ // If Debug mode set, panic. Not implemented
+ if (BX_USB_THIS hub[0].usb_command.debug)
+ BX_PANIC(("Software set DEBUG bit in Command register. Not implemented"));
+
+ break;
+
+ case 0x02: // status register (16-bit) (R/WC)
+ if (value & 0xFFC0)
+ BX_ERROR(("write to status register with bits 15:6 not zero: 0x%04x", value));
+
+ BX_USB_THIS hub[0].usb_status.host_halted = (value & 0x20) ? 0: BX_USB_THIS hub[0].usb_status.host_halted;
+ BX_USB_THIS hub[0].usb_status.host_error = (value & 0x10) ? 0: BX_USB_THIS hub[0].usb_status.host_error;
+ BX_USB_THIS hub[0].usb_status.pci_error = (value & 0x08) ? 0: BX_USB_THIS hub[0].usb_status.pci_error;
+ BX_USB_THIS hub[0].usb_status.resume = (value & 0x04) ? 0: BX_USB_THIS hub[0].usb_status.resume;
+ BX_USB_THIS hub[0].usb_status.error_interrupt = (value & 0x02) ? 0: BX_USB_THIS hub[0].usb_status.error_interrupt;
+ BX_USB_THIS hub[0].usb_status.interrupt = (value & 0x01) ? 0: BX_USB_THIS hub[0].usb_status.interrupt;
+ break;
+
+ case 0x04: // interrupt enable register (16-bit)
+ if (value & 0xFFF0)
+ BX_ERROR(("write to interrupt enable register with bits 15:4 not zero: 0x%04x", value));
+
+ BX_USB_THIS hub[0].usb_enable.short_packet = (value & 0x08) ? 1: 0;
+ BX_USB_THIS hub[0].usb_enable.on_complete = (value & 0x04) ? 1: 0;
+ BX_USB_THIS hub[0].usb_enable.resume = (value & 0x02) ? 1: 0;
+ BX_USB_THIS hub[0].usb_enable.timeout_crc = (value & 0x01) ? 1: 0;
+
+ // For now, we will just ignore these being set since we never raise the IRQ
+
+ break;
+
+ case 0x06: // frame number register (16-bit)
+ if (value & 0xF800)
+ BX_ERROR(("write to frame number register with bits 15:11 not zero: 0x%04x", value));
+
+ if (BX_USB_THIS hub[0].usb_status.host_halted)
+ BX_USB_THIS hub[0].usb_frame_num.frame_num = value;
+ else
+ // ignored by the hardward, but lets report it anyway
+ BX_ERROR(("write to frame number register with STATUS.HALTED == 0"));
+
+ break;
+
+ case 0x08: // frame base register (32-bit)
+ if (value & 0xFFF)
+ BX_PANIC(("write to frame base register with bits 11:0 not zero: 0x%08x", value));
+
+ BX_USB_THIS hub[0].usb_frame_base.frame_base = value;
+ break;
+
+ case 0x0C: // start of Frame Modify register (8-bit)
+ if (value & 0x80)
+ BX_ERROR(("write to SOF Modify register with bit 7 not zero: 0x%04x", value));
+
+ BX_USB_THIS hub[0].usb_sof.sof_timing = value;
+ break;
+
+ case 0x10: // port0
+ case 0x12: // port1
+ port = (offset & 0x0F) >> 1;
+ if (port < USB_NUM_PORTS) {
+ if (value & ((1<<5) | (1<<4) | (1<<0)))
+ BX_PANIC(("write to one or more read-only bits in port%d register: 0x%04x", port, value));
+ if (!(value & (1<<7)))
+ BX_ERROR(("write to port%d register bit 7 = 0", port));
+ if (value & (1<<8))
+ BX_INFO(("write to bit 8 in port%d register ignored", port));
+ if (value & (1<<2))
+ BX_INFO(("port%d enabled ignored. Not implemented", port));
+ if ((value & (1<<12)) && BX_USB_THIS hub[0].usb_command.suspend)
+ BX_ERROR(("write to port%d register bit 12 when in Global-Suspend", port));
+
+ BX_USB_THIS hub[0].usb_port[port].suspend = (value & (1<<12)) ? 1 : 0;
+ BX_USB_THIS hub[0].usb_port[port].reset = (value & (1<<9)) ? 1 : 0;
+ BX_USB_THIS hub[0].usb_port[port].resume = (value & (1<<6)) ? 1 : 0;
+ BX_USB_THIS hub[0].usb_port[port].able_changed = (value & (1<<3)) ? 0 : BX_USB_THIS hub[0].usb_port[0].able_changed;
+ BX_USB_THIS hub[0].usb_port[port].enabled = (value & (1<<2)) ? 1 : 0;
+ BX_USB_THIS hub[0].usb_port[port].connect_changed = (value & (1<<1)) ? 0 : BX_USB_THIS hub[0].usb_port[0].connect_changed;
+ break;
+ }
+ // else fall through to default
+
+ default:
+ BX_PANIC(("unsupported io write to address=0x%04x!", (unsigned) address));
+ break;
+ }
+}
+
+void bx_pciusb_c::usb_timer_handler(void *this_ptr)
+{
+ bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
+ class_ptr->usb_timer();
+}
+
+// Called once every 1ms
+void bx_pciusb_c::usb_timer(void)
+{
+ int i;
+
+ // The Frame Number Register is incremented every 1ms ?????????
+ // Needs more work and investigation on this.
+ BX_USB_THIS hub[0].usb_frame_num.frame_num++;
+ BX_USB_THIS hub[0].usb_frame_num.frame_num &= (1024-1);
+
+ // If the "global reset" bit was set by software, we need
+ // to set the reset bit in each "active" port for 10ms
+ if (BX_USB_THIS global_reset) {
+ for (i=0; i<USB_NUM_PORTS; i++) {
+ BX_USB_THIS hub[0].usb_port[i].able_changed = 0;
+ BX_USB_THIS hub[0].usb_port[i].connect_changed = 0;
+ BX_USB_THIS hub[0].usb_port[i].enabled = 0;
+ BX_USB_THIS hub[0].usb_port[i].line_dminus = 0;
+ BX_USB_THIS hub[0].usb_port[i].line_dplus = 0;
+ BX_USB_THIS hub[0].usb_port[i].low_speed = 0;
+ BX_USB_THIS hub[0].usb_port[i].reset = 1;
+ BX_USB_THIS hub[0].usb_port[i].resume = 0;
+ BX_USB_THIS hub[0].usb_port[i].status = 0;
+ BX_USB_THIS hub[0].usb_port[i].suspend = 0;
+ }
+ BX_USB_THIS global_reset--;
+ } else {
+ for (i=0; i<USB_NUM_PORTS; i++)
+ BX_USB_THIS hub[0].usb_port[i].reset = 0;
+ }
+
+ // If command.schedule = 0, then we need to set Status.Halted
+ if (!BX_USB_THIS hub[0].usb_command.schedule)
+ BX_USB_THIS hub[0].usb_status.host_halted = 1;
+
+
+ // TODO:
+ // If ins Global_Suspend mode and any of usb_port[i] bits 6,3, or 1 are set,
+ // we need to issue a Global_Resume (set the global resume bit).
+ // However, since we don't do anything, let's not.
+
+}
+
+ // static pci configuration space read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pciusb_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len)
+{
+#if !BX_USE_PCIUSB_SMF
+ bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
+
+ return class_ptr->pci_read(address, io_len);
+}
+
+
+ Bit32u
+bx_pciusb_c::pci_read(Bit8u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCIUSB_SMF
+
+ Bit32u value = 0;
+
+ if (io_len > 4 || io_len == 0) {
+ BX_ERROR(("Experimental USB PCI read register 0x%02x, len=%u !",
+ (unsigned) address, (unsigned) io_len));
+ return 0xffffffff;
+ }
+
+ const char* pszName = " ";
+ switch (address) {
+ case 0x00: if (io_len == 2) {
+ pszName = "(vendor id) ";
+ } else if (io_len == 4) {
+ pszName = "(vendor + device) ";
+ }
+ break;
+ case 0x04: if (io_len == 2) {
+ pszName = "(command) ";
+ } else if (io_len == 4) {
+ pszName = "(command+status) ";
+ }
+ break;
+ case 0x08: if (io_len == 1) {
+ pszName = "(revision id) ";
+ } else if (io_len == 4) {
+ pszName = "(rev.+class code) ";
+ }
+ break;
+ case 0x0c: pszName = "(cache line size) "; break;
+ case 0x20: pszName = "(base address) "; break;
+ case 0x28: pszName = "(cardbus cis) "; break;
+ case 0x2c: pszName = "(subsys. vendor+) "; break;
+ case 0x30: pszName = "(rom base) "; break;
+ case 0x3c: pszName = "(interrupt line+) "; break;
+ case 0x3d: pszName = "(interrupt pin) "; break;
+ }
+
+ // This odd code is to display only what bytes actually were read.
+ char szTmp[9];
+ char szTmp2[3];
+ szTmp[0] = '\0';
+ szTmp2[0] = '\0';
+ for (unsigned i=0; i<io_len; i++) {
+ value |= (BX_USB_THIS hub[0].pci_conf[address+i] << (i*8));
+ sprintf(szTmp2, "%02x", (BX_USB_THIS hub[0].pci_conf[address+i]));
+ strrev(szTmp2);
+ strcat(szTmp, szTmp2);
+ }
+ strrev(szTmp);
+ BX_DEBUG(("Experimental USB PCI read register 0x%02x %svalue 0x%s",
+ address, pszName, szTmp));
+ return value;
+}
+
+
+ // static pci configuration space write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pciusb_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PCIUSB_SMF
+ bx_pciusb_c *class_ptr = (bx_pciusb_c *) this_ptr;
+
+ class_ptr->pci_write(address, value, io_len);
+}
+
+ void
+bx_pciusb_c::pci_write(Bit8u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCIUSB_SMF
+
+ if (io_len > 4 || io_len == 0) {
+ BX_ERROR(("Experimental USB PCI write register 0x%02x, len=%u !",
+ (unsigned) address, (unsigned) io_len));
+ return;
+ }
+
+ // This odd code is to display only what bytes actually were written.
+ char szTmp[9];
+ char szTmp2[3];
+ szTmp[0] = '\0';
+ szTmp2[0] = '\0';
+ for (unsigned i=0; i<io_len; i++) {
+ const Bit8u value8 = (value >> (i*8)) & 0xFF;
+ switch (address+i) {
+ case 0x20: // Base address
+ BX_USB_THIS hub[0].pci_conf[address+i] = (value8 & 0xe0) | 0x01;
+ sprintf(szTmp2, "%02x", (value8 & 0xe0) | 0x01);
+ break;
+ case 0x10: // Reserved
+ case 0x11: //
+ case 0x12: //
+ case 0x13: //
+ case 0x14: //
+ case 0x15: //
+ case 0x16: //
+ case 0x17: //
+ case 0x18: //
+ case 0x19: //
+ case 0x1a: //
+ case 0x1b: //
+ case 0x1c: //
+ case 0x1d: //
+ case 0x1e: //
+ case 0x1f: //
+ case 0x22: // Always 0
+ case 0x23: //
+ case 0x24: // Reserved
+ case 0x25: //
+ case 0x26: //
+ case 0x27: //
+ case 0x30: // Oh, no, you're not writing to rom_base!
+ case 0x31: //
+ case 0x32: //
+ case 0x33: //
+ case 0x3d: //
+ case 0x05: // disallowing write to command hi-byte
+ case 0x06: // disallowing write to status lo-byte (is that expected?)
+ strcpy(szTmp2, "..");
+ break;
+ default:
+ BX_USB_THIS hub[0].pci_conf[address+i] = value8;
+ sprintf(szTmp2, "%02x", value8);
+ }
+ strrev(szTmp2);
+ strcat(szTmp, szTmp2);
+ }
+ strrev(szTmp);
+ BX_DEBUG(("Experimental USB PCI write register 0x%02x value 0x%s", address, szTmp));
+}
+
+#endif // BX_PCI_SUPPORT && BX_PCI_USB_SUPPORT
diff --git a/tools/ioemu/iodev/pciusb.h b/tools/ioemu/iodev/pciusb.h
new file mode 100644
index 0000000000..be2532c86f
--- /dev/null
+++ b/tools/ioemu/iodev/pciusb.h
@@ -0,0 +1,195 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pciusb.h,v 1.1 2003/01/28 16:58:10 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2003 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// Benjamin D Lunt (fys@cybertrails.com) coded most of this usb emulation.
+// I hope to add to this code to make it more functionable.
+//
+
+#if BX_USE_PCIUSB_SMF
+# define BX_USB_THIS theUSBDevice->
+#else
+# define BX_USB_THIS this->
+#endif
+
+#define BX_USB_MAXDEV 1
+#define BX_USB_CONFDEV 1 /* only 1 USB hub currently */
+
+#define USB_NUM_PORTS 2 /* UHCI supports 2 ports per root hub */
+
+typedef struct {
+
+ Bit16u base_ioaddr;
+ Bit8u irq;
+ int timer_index;
+
+ // Registers
+ // Base + 0x00 Command register
+ // Base + 0x02 Status register
+ // Base + 0x04 Interrupt Enable register
+ // Base + 0x06 Frame Number register
+ // Base + 0x08 Frame Base Register (32-bit)
+ // Base + 0x0C Start of Frame Modify register
+ // Base + 0x0D
+ // Base + 0x0E
+ // Base + 0x0F
+ // Base + 0x10 Eight(?) 16-bit ports (one for each port on hub)
+
+ // Bit reps of registers above
+ // Command Register
+ // Bits 15-8 are reserved
+ // Bit 7 = Maximum packet size
+ // Bit 6 = Host Controller has been configured (set by software)
+ // Bit 5 = software debug mode
+ // Bit 4 = force global resume
+ // Bit 3 = enter global suspend mode
+ // Bit 2 = global reset
+ // Bit 1 = host controller reset
+ // Bit 0 = run/stop schedule
+ struct {
+ bx_bool max_packet_size; //(bit 7) 0 = 32 bytes, 1 = 64 bytes
+ bx_bool configured; //(bit 6)
+ bx_bool debug; //(bit 5)
+ bx_bool resume; //(bit 4)
+ bx_bool suspend; //(bit 3)
+ bx_bool reset; //(bit 2)
+ bx_bool host_reset; //(bit 1)
+ bx_bool schedule; //(bit 0) 0 = Stop, 1 = Run
+ } usb_command;
+
+ // Status Register
+ // Bits 15-6 are reserved
+ // Bit 5 = Host controller halted
+ // Bit 4 = Host controller process error
+ // Bit 3 = PCI Bus error
+ // Bit 2 = resume received
+ // Bit 1 = USB error interrupt
+ // Bit 0 = USB interrupt
+ struct {
+ bx_bool host_halted; //(bit 5)
+ bx_bool host_error; //(bit 4)
+ bx_bool pci_error; //(bit 3)
+ bx_bool resume; //(bit 2)
+ bx_bool error_interrupt; //(bit 1)
+ bx_bool interrupt; //(bit 0)
+ } usb_status;
+
+ // Interrupt Enable Register
+ // Bits 15-4 are reserved
+ // Bit 3 = enable short packet interrupts
+ // Bit 2 = enable interrupt On Complete
+ // Bit 1 = enable resume
+ // Bit 0 = enable timeout/crc
+ struct {
+ bx_bool short_packet; //(bit 3)
+ bx_bool on_complete; //(bit 2)
+ bx_bool resume; //(bit 1)
+ bx_bool timeout_crc; //(bit 0)
+ } usb_enable;
+
+ // Frame Number Register
+ // Bits 15-11 are reserved
+ // Bits 10-0 Frame List Current Index/Frame Number
+ struct {
+ Bit16u frame_num;
+ } usb_frame_num;
+
+ // Frame List Base Address Register
+ // Bits 31-12 Base
+ // Bits 11-0 *must* be zeros when written to
+ struct {
+ Bit32u frame_base;
+ } usb_frame_base;
+
+ // Start of Frame Modify Register
+ // Bit 7 reserved
+ // Bits 6-0 SOF timing value (default 64)
+ // SOF cycle time equals 11936+timing value
+ struct {
+ Bit8u sof_timing;
+ } usb_sof;
+
+ // Port Register (0-1)
+ // Bits 15-13 are reserved
+ // Bit 12 suspend port
+ // Bit 11-10 are reserved
+ // Bit 9 port in reset state
+ // Bit 8 low-speed device is attached (read-only)
+ // Bit 7 reserved
+ // Bit 6 resume detected (read-only)
+ // Bit 5 line-status D+ (read-only)
+ // Bit 4 line-status D- (read-only)
+ // Bit 3 port enabled/disable status has changed
+ // (write 1 to this bit to clear it)
+ // Bit 2 port is enabled
+ // Bit 1 connect status has changed
+ // (write 1 to this bit to clear it)
+ // Bit 0 current connect status (read-only)
+ // Can only write in WORD sizes (Read in byte sizes???)
+ struct {
+ bx_bool suspend;
+ bx_bool reset;
+ bx_bool low_speed;
+ bx_bool resume;
+ bx_bool line_dplus;
+ bx_bool line_dminus;
+ bx_bool able_changed;
+ bx_bool enabled;
+ bx_bool connect_changed;
+ bx_bool status;
+ } usb_port[USB_NUM_PORTS];
+
+ Bit8u pci_conf[256];
+
+} bx_usb_t;
+
+
+class bx_pciusb_c : public bx_devmodel_c
+{
+public:
+ bx_pciusb_c(void);
+ ~bx_pciusb_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+
+private:
+
+ bx_usb_t hub[BX_USB_MAXDEV];
+ Bit8u global_reset;
+
+ static void usb_timer_handler(void *);
+ void usb_timer(void);
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+ static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len);
+ static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len);
+#if !BX_USE_PCIUSB_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+ Bit32u pci_read(Bit8u address, unsigned io_len);
+ void pci_write(Bit8u address, Bit32u value, unsigned io_len);
+#endif
+};
diff --git a/tools/ioemu/iodev/pcivga.cc b/tools/ioemu/iodev/pcivga.cc
new file mode 100644
index 0000000000..4d999be74d
--- /dev/null
+++ b/tools/ioemu/iodev/pcivga.cc
@@ -0,0 +1,248 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pcivga.cc,v 1.2 2003/01/23 19:31:28 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002,2003 Mike Nordell
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+//
+// Experimental PCI VGA adapter
+//
+
+// Note: This "driver" was created for the SOLE PURPOSE of getting BeOS
+// to boot. It currently does NOTHING more than presenting a generic VGA
+// device on the PCI bus. ALL gfx in/out-put is still handled by the VGA code.
+// Furthermore, almost all of the PCI registers are currently acting like RAM.
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if BX_PCI_SUPPORT && BX_PCI_VGA_SUPPORT
+
+#define LOG_THIS thePciVgaAdapter->
+
+bx_pcivga_c* thePciVgaAdapter = 0;
+
+ int
+libpcivga_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ thePciVgaAdapter = new bx_pcivga_c ();
+ bx_devices.pluginPciVgaAdapter = thePciVgaAdapter;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePciVgaAdapter, BX_PLUGIN_PCIVGA);
+ return 0; // Success
+}
+
+ void
+libpcivga_LTX_plugin_fini(void)
+{
+}
+
+
+bx_pcivga_c::bx_pcivga_c(void)
+{
+ put("PCIVGA");
+ settype(PCIVGALOG);
+}
+
+bx_pcivga_c::~bx_pcivga_c(void)
+{
+ // nothing for now
+ BX_DEBUG(("Exit."));
+}
+
+
+ void
+bx_pcivga_c::init(void)
+{
+ // called once when bochs initializes
+
+ DEV_register_pci_handlers(this,
+ pci_read_handler,
+ pci_write_handler,
+ BX_PCI_DEVICE(2,0),
+ "Experimental PCI VGA");
+
+ for (unsigned i=0; i<256; i++) {
+ BX_PCIVGA_THIS s.pci_conf[i] = 0x0;
+ }
+
+ // readonly registers
+ static const struct init_vals_t {
+ unsigned addr;
+ unsigned char val;
+ } init_vals[] = {
+ // Note that the values for vendor and device id are selected at random!
+ // There might actually be "real" values for "experimental" vendor and
+ // device that should be used!
+ { 0x00, 0x34 }, { 0x01, 0x12 }, // 0x1234 - experimental vendor
+ { 0x02, 0x11 }, { 0x03, 0x11 }, // 0x1111 - experimental device
+ { 0x0a, 0x00 }, // class_sub VGA controller
+ { 0x0b, 0x03 }, // class_base display
+ { 0x0e, 0x00 } // header_type_generic
+ };
+ for (unsigned i = 0; i < sizeof(init_vals) / sizeof(*init_vals); ++i) {
+ BX_PCIVGA_THIS s.pci_conf[init_vals[i].addr] = init_vals[i].val;
+ }
+}
+
+ void
+bx_pcivga_c::reset(unsigned type)
+{
+ static const struct reset_vals_t {
+ unsigned addr;
+ unsigned char val;
+ } reset_vals[] = {
+ { 0x04, 0x01 }, { 0x05, 0x00 }, // command_io
+ { 0x06, 0x00 }, { 0x07, 0x02 } // status_devsel_medium
+ };
+ for (unsigned i = 0; i < sizeof(reset_vals) / sizeof(*reset_vals); ++i) {
+ BX_PCIVGA_THIS s.pci_conf[reset_vals[i].addr] = reset_vals[i].val;
+ }
+}
+
+
+ // static pci configuration space read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pcivga_c::pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len)
+{
+#if !BX_USE_PCIVGA_SMF
+ bx_pcivga_c *class_ptr = (bx_pcivga_c *) this_ptr;
+
+ return class_ptr->pci_read(address, io_len);
+}
+
+
+ Bit32u
+bx_pcivga_c::pci_read(Bit8u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCIVGA_SMF
+
+ Bit32u value = 0;
+
+ if (io_len > 4 || io_len == 0) {
+ BX_DEBUG(("Experimental PCIVGA read register 0x%02x, len=%u !",
+ (unsigned) address, (unsigned) io_len));
+ return 0xffffffff;
+ }
+
+ const char* pszName = " ";
+ switch (address) {
+ case 0x00: if (io_len == 2) {
+ pszName = "(vendor id) ";
+ } else if (io_len == 4) {
+ pszName = "(vendor + device) ";
+ }
+ break;
+ case 0x04: if (io_len == 2) {
+ pszName = "(command) ";
+ } else if (io_len == 4) {
+ pszName = "(command+status) ";
+ }
+ break;
+ case 0x08: if (io_len == 1) {
+ pszName = "(revision id) ";
+ } else if (io_len == 4) {
+ pszName = "(rev.+class code) ";
+ }
+ break;
+ case 0x0c: pszName = "(cache line size) "; break;
+ case 0x28: pszName = "(cardbus cis) "; break;
+ case 0x2c: pszName = "(subsys. vendor+) "; break;
+ case 0x30: pszName = "(rom base) "; break;
+ case 0x3c: pszName = "(interrupt line+) "; break;
+ case 0x3d: pszName = "(interrupt pin) "; break;
+ }
+
+ // This odd code is to display only what bytes actually were read.
+ char szTmp[9];
+ char szTmp2[3];
+ szTmp[0] = '\0';
+ szTmp2[0] = '\0';
+ for (unsigned i=0; i<io_len; i++) {
+ value |= (BX_PCIVGA_THIS s.pci_conf[address+i] << (i*8));
+
+ sprintf(szTmp2, "%02x", (BX_PCIVGA_THIS s.pci_conf[address+i]));
+ strrev(szTmp2);
+ strcat(szTmp, szTmp2);
+ }
+ strrev(szTmp);
+ BX_DEBUG(("Experimental PCIVGA read register 0x%02x %svalue 0x%s",
+ address, pszName, szTmp));
+ return value;
+}
+
+
+ // static pci configuration space write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pcivga_c::pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PCIVGA_SMF
+ bx_pcivga_c *class_ptr = (bx_pcivga_c *) this_ptr;
+
+ class_ptr->pci_write(address, value, io_len);
+}
+
+ void
+bx_pcivga_c::pci_write(Bit8u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PCIVGA_SMF
+
+ if (io_len > 4 || io_len == 0) {
+ BX_DEBUG(("Experimental PCIVGA write register 0x%02x, len=%u !",
+ (unsigned) address, (unsigned) io_len));
+ return;
+ }
+
+ // This odd code is to display only what bytes actually were written.
+ char szTmp[9];
+ char szTmp2[3];
+ szTmp[0] = '\0';
+ szTmp2[0] = '\0';
+ for (unsigned i=0; i<io_len; i++) {
+ const Bit8u value8 = (value >> (i*8)) & 0xFF;
+ switch (address+i) {
+ case 0x30: // Oh, no, you're not writing to rom_base!
+ case 0x31: //
+ case 0x32: //
+ case 0x33: //
+ case 0x04: // disallowing write to command
+ case 0x06: // disallowing write to status lo-byte (is that expected?)
+ strcpy(szTmp2, "..");
+ break;
+ default:
+ BX_PCIVGA_THIS s.pci_conf[address+i] = value8;
+ sprintf(szTmp2, "%02x", value8);
+ }
+ strrev(szTmp2);
+ strcat(szTmp, szTmp2);
+ }
+ strrev(szTmp);
+ BX_DEBUG(("Experimental PCIVGA write register 0x%02x value 0x%s", address, szTmp));
+}
+
+#endif // BX_PCI_SUPPORT && BX_PCI_VGA_SUPPORT
diff --git a/tools/ioemu/iodev/pcivga.h b/tools/ioemu/iodev/pcivga.h
new file mode 100644
index 0000000000..15bd986a09
--- /dev/null
+++ b/tools/ioemu/iodev/pcivga.h
@@ -0,0 +1,48 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pcivga.h,v 1.3 2003/01/27 21:11:55 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002,2003 Mike Nordell
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#if BX_USE_PCIVGA_SMF
+# define BX_PCIVGA_THIS thePciVgaAdapter->
+#else
+# define BX_PCIVGA_THIS this->
+#endif
+
+
+class bx_pcivga_c : public bx_devmodel_c
+{
+public:
+ bx_pcivga_c(void);
+ ~bx_pcivga_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+
+private:
+
+ struct {
+ Bit8u pci_conf[256];
+ } s;
+
+ static Bit32u pci_read_handler(void *this_ptr, Bit8u address, unsigned io_len);
+ static void pci_write_handler(void *this_ptr, Bit8u address, Bit32u value, unsigned io_len);
+#if !BX_USE_PCIVGA_SMF
+ Bit32u pci_read(Bit8u address, unsigned io_len);
+ void pci_write(Bit8u address, Bit32u value, unsigned io_len);
+#endif
+};
diff --git a/tools/ioemu/iodev/pic.cc b/tools/ioemu/iodev/pic.cc
new file mode 100644
index 0000000000..f4455508b6
--- /dev/null
+++ b/tools/ioemu/iodev/pic.cc
@@ -0,0 +1,887 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pic.cc,v 1.33 2003/08/05 09:19:36 akrisak Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS thePic->
+
+
+
+bx_pic_c *thePic = NULL;
+
+ int
+libpic_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ thePic = new bx_pic_c ();
+ bx_devices.pluginPicDevice = thePic;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, thePic, BX_PLUGIN_PIC);
+ return(0); // Success
+}
+
+ void
+libpic_LTX_plugin_fini(void)
+{
+}
+
+
+bx_pic_c::bx_pic_c(void)
+{
+ put("PIC");
+ settype(PICLOG);
+}
+
+bx_pic_c::~bx_pic_c(void)
+{
+ // nothing for now
+}
+
+
+ void
+bx_pic_c::init(void)
+{
+ /* 8259 PIC (Programmable Interrupt Controller) */
+ DEV_register_ioread_handler(this, read_handler, 0x0020, "8259 PIC", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0021, "8259 PIC", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x00A0, "8259 PIC", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x00A1, "8259 PIC", 1);
+
+ DEV_register_iowrite_handler(this, write_handler, 0x0020, "8259 PIC", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0021, "8259 PIC", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x00A0, "8259 PIC", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x00A1, "8259 PIC", 1);
+
+
+ BX_PIC_THIS s.master_pic.single_PIC = 0;
+ BX_PIC_THIS s.master_pic.interrupt_offset = 0x08; /* IRQ0 = INT 0x08 */
+ /* slave PIC connected to IRQ2 of master */
+ BX_PIC_THIS s.master_pic.u.slave_connect_mask = 0x04;
+ BX_PIC_THIS s.master_pic.sfnm = 0; /* normal nested mode */
+ BX_PIC_THIS s.master_pic.buffered_mode = 0; /* unbuffered mode */
+ BX_PIC_THIS s.master_pic.master_slave = 0; /* no meaning, buffered_mode=0 */
+ BX_PIC_THIS s.master_pic.auto_eoi = 0; /* manual EOI from CPU */
+ BX_PIC_THIS s.master_pic.imr = 0xFF; /* all IRQ's initially masked */
+ BX_PIC_THIS s.master_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.master_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.master_pic.read_reg_select = 0; /* IRR */
+ BX_PIC_THIS s.master_pic.irq = 0;
+ BX_PIC_THIS s.master_pic.INT = 0;
+ BX_PIC_THIS s.master_pic.init.in_init = 0;
+ BX_PIC_THIS s.master_pic.init.requires_4 = 0;
+ BX_PIC_THIS s.master_pic.init.byte_expected = 0;
+ BX_PIC_THIS s.master_pic.special_mask = 0;
+ BX_PIC_THIS s.master_pic.lowest_priority = 7;
+ BX_PIC_THIS s.master_pic.polled = 0;
+ BX_PIC_THIS s.master_pic.rotate_on_autoeoi = 0;
+
+ BX_PIC_THIS s.slave_pic.single_PIC = 0;
+ BX_PIC_THIS s.slave_pic.interrupt_offset = 0x70; /* IRQ8 = INT 0x70 */
+ BX_PIC_THIS s.slave_pic.u.slave_id = 0x02; /* slave PIC connected to IRQ2 of master */
+ BX_PIC_THIS s.slave_pic.sfnm = 0; /* normal nested mode */
+ BX_PIC_THIS s.slave_pic.buffered_mode = 0; /* unbuffered mode */
+ BX_PIC_THIS s.slave_pic.master_slave = 0; /* no meaning, buffered_mode=0 */
+ BX_PIC_THIS s.slave_pic.auto_eoi = 0; /* manual EOI from CPU */
+ BX_PIC_THIS s.slave_pic.imr = 0xFF; /* all IRQ's initially masked */
+ BX_PIC_THIS s.slave_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.slave_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 0; /* IRR */
+ BX_PIC_THIS s.slave_pic.irq = 0;
+ BX_PIC_THIS s.slave_pic.INT = 0;
+ BX_PIC_THIS s.slave_pic.init.in_init = 0;
+ BX_PIC_THIS s.slave_pic.init.requires_4 = 0;
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 0;
+ BX_PIC_THIS s.slave_pic.special_mask = 0;
+ BX_PIC_THIS s.slave_pic.lowest_priority = 7;
+ BX_PIC_THIS s.slave_pic.polled = 0;
+ BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = 0;
+
+ for (unsigned i=0; i<8; i++) { /* all IRQ lines low */
+ BX_PIC_THIS s.master_pic.IRQ_line[i] = 0;
+ BX_PIC_THIS s.slave_pic.IRQ_line[i] = 0;
+ }
+}
+
+ void
+bx_pic_c::reset(unsigned type)
+{
+}
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pic_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_PIC_SMF
+ bx_pic_c *class_ptr = (bx_pic_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+
+ Bit32u
+bx_pic_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIC_SMF
+
+ BX_DEBUG(("IO read from %04x", (unsigned) address));
+
+ /*
+ 8259A PIC
+ */
+
+ if((address == 0x20 || address == 0x21) && BX_PIC_THIS s.master_pic.polled) {
+ // In polled mode. Treat this as an interrupt acknowledge
+ clear_highest_interrupt(& BX_PIC_THIS s.master_pic);
+ BX_PIC_THIS s.master_pic.polled = 0;
+ service_master_pic();
+ return io_len==1?BX_PIC_THIS s.master_pic.irq:(BX_PIC_THIS s.master_pic.irq)<<8|(BX_PIC_THIS s.master_pic.irq); // Return the current irq requested
+ }
+
+ if((address == 0xa0 || address == 0xa1) && BX_PIC_THIS s.slave_pic.polled) {
+ // In polled mode. Treat this as an interrupt acknowledge
+ clear_highest_interrupt(& BX_PIC_THIS s.slave_pic);
+ BX_PIC_THIS s.slave_pic.polled = 0;
+ service_slave_pic();
+ return io_len==1?BX_PIC_THIS s.slave_pic.irq:(BX_PIC_THIS s.slave_pic.irq)<<8|(BX_PIC_THIS s.slave_pic.irq); // Return the current irq requested
+ }
+
+
+ switch (address) {
+ case 0x20:
+ if (BX_PIC_THIS s.master_pic.read_reg_select) { /* ISR */
+ BX_DEBUG(("read master ISR = %02x",
+ (unsigned) BX_PIC_THIS s.master_pic.isr));
+ return(BX_PIC_THIS s.master_pic.isr);
+ }
+ else { /* IRR */
+ BX_DEBUG(("read master IRR = %02x",
+ (unsigned) BX_PIC_THIS s.master_pic.irr));
+ return(BX_PIC_THIS s.master_pic.irr);
+ }
+ break;
+ case 0x21:
+ BX_DEBUG(("read master IMR = %02x",
+ (unsigned) BX_PIC_THIS s.master_pic.imr));
+ return(BX_PIC_THIS s.master_pic.imr);
+ break;
+ case 0xA0:
+ if (BX_PIC_THIS s.slave_pic.read_reg_select) { /* ISR */
+ BX_DEBUG(("read slave ISR = %02x",
+ (unsigned) BX_PIC_THIS s.slave_pic.isr));
+ return(BX_PIC_THIS s.slave_pic.isr);
+ }
+ else { /* IRR */
+ BX_DEBUG(("read slave IRR = %02x",
+ (unsigned) BX_PIC_THIS s.slave_pic.irr));
+ return(BX_PIC_THIS s.slave_pic.irr);
+ }
+ break;
+ case 0xA1:
+ BX_DEBUG(("read slave IMR = %02x",
+ (unsigned) BX_PIC_THIS s.slave_pic.imr));
+ return(BX_PIC_THIS s.slave_pic.imr);
+ break;
+ }
+
+ BX_PANIC(("io read to address %04x", (unsigned) address));
+ return(0); /* default if not found above */
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pic_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_PIC_SMF
+ bx_pic_c *class_ptr = (bx_pic_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_pic_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIC_SMF
+
+ BX_DEBUG(("IO write to %04x = %02x", (unsigned) address, (unsigned) value));
+
+ /*
+ 8259A PIC
+ */
+
+ switch (address) {
+ case 0x20:
+ if (value & 0x10) { /* initialization command 1 */
+ BX_DEBUG(("master: init command 1 found"));
+ BX_DEBUG((" requires 4 = %u", (unsigned) (value & 0x01) ));
+ BX_DEBUG((" cascade mode: [0=cascade,1=single] %u",
+ (unsigned) ((value & 0x02) >> 1)));
+ BX_PIC_THIS s.master_pic.init.in_init = 1;
+ BX_PIC_THIS s.master_pic.init.requires_4 = (value & 0x01);
+ BX_PIC_THIS s.master_pic.init.byte_expected = 2; /* operation command 2 */
+ BX_PIC_THIS s.master_pic.imr = 0x00; /* clear the irq mask register */
+ BX_PIC_THIS s.master_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.master_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.master_pic.lowest_priority = 7;
+ BX_PIC_THIS s.master_pic.INT = 0; /* reprogramming clears previous INTR request */
+ BX_PIC_THIS s.master_pic.auto_eoi = 0;
+ BX_PIC_THIS s.master_pic.rotate_on_autoeoi = 0;
+ if (value & 0x02)
+ BX_PANIC(("master: ICW1: single mode not supported"));
+ if (value & 0x08) {
+ BX_PANIC(("master: ICW1: level sensitive mode not supported"));
+ }
+ else {
+ BX_DEBUG(("master: ICW1: edge triggered mode selected"));
+ }
+ BX_SET_INTR(0);
+ return;
+ }
+
+ if ( (value & 0x18) == 0x08 ) { /* OCW3 */
+ Bit8u special_mask, poll, read_op;
+
+ special_mask = (value & 0x60) >> 5;
+ poll = (value & 0x04) >> 2;
+ read_op = (value & 0x03);
+ if (poll) {
+ BX_PIC_THIS s.master_pic.polled = 1;
+ return;
+ }
+ if (read_op == 0x02) /* read IRR */
+ BX_PIC_THIS s.master_pic.read_reg_select = 0;
+ else if (read_op == 0x03) /* read ISR */
+ BX_PIC_THIS s.master_pic.read_reg_select = 1;
+ if (special_mask == 0x02) { /* cancel special mask */
+ BX_PIC_THIS s.master_pic.special_mask = 0;
+ }
+ else if (special_mask == 0x03) { /* set specific mask */
+ BX_PIC_THIS s.master_pic.special_mask = 1;
+ service_master_pic();
+ }
+ return;
+ }
+
+ /* OCW2 */
+ switch (value) {
+ case 0x00: // Rotate in auto eoi mode clear
+ case 0x80: // Rotate in auto eoi mode set
+ BX_PIC_THIS s.master_pic.rotate_on_autoeoi = (value != 0);
+ break;
+ case 0x0A: /* select read interrupt request register */
+ BX_PIC_THIS s.master_pic.read_reg_select = 0;
+ break;
+ case 0x0B: /* select read interrupt in-service register */
+ BX_PIC_THIS s.master_pic.read_reg_select = 1;
+ break;
+
+ case 0xA0: // Rotate on non-specific end of interrupt
+ case 0x20: /* end of interrupt command */
+
+ clear_highest_interrupt(& BX_PIC_THIS s.master_pic);
+
+ if(value == 0xA0) {// Rotate in Auto-EOI mode
+ BX_PIC_THIS s.master_pic.lowest_priority ++;
+ if(BX_PIC_THIS s.master_pic.lowest_priority > 7)
+ BX_PIC_THIS s.master_pic.lowest_priority = 0;
+ }
+
+ service_master_pic();
+ break;
+
+ case 0x40: // Intel PIC spec-sheet seems to indicate this should be ignored
+ BX_INFO(("IRQ no-op"));
+ break;
+
+ case 0x60: /* specific EOI 0 */
+ case 0x61: /* specific EOI 1 */
+ case 0x62: /* specific EOI 2 */
+ case 0x63: /* specific EOI 3 */
+ case 0x64: /* specific EOI 4 */
+ case 0x65: /* specific EOI 5 */
+ case 0x66: /* specific EOI 6 */
+ case 0x67: /* specific EOI 7 */
+ BX_PIC_THIS s.master_pic.isr &= ~(1 << (value-0x60));
+ service_master_pic();
+ break;
+
+ // IRQ lowest priority commands
+ case 0xC0: // 0 7 6 5 4 3 2 1
+ case 0xC1: // 1 0 7 6 5 4 3 2
+ case 0xC2: // 2 1 0 7 6 5 4 3
+ case 0xC3: // 3 2 1 0 7 6 5 4
+ case 0xC4: // 4 3 2 1 0 7 6 5
+ case 0xC5: // 5 4 3 2 1 0 7 6
+ case 0xC6: // 6 5 4 3 2 1 0 7
+ case 0xC7: // 7 6 5 4 3 2 1 0
+ BX_INFO(("IRQ lowest command 0x%x", value));
+ BX_PIC_THIS s.master_pic.lowest_priority = value - 0xC0;
+ break;
+
+ case 0xE0: // specific EOI and rotate 0
+ case 0xE1: // specific EOI and rotate 1
+ case 0xE2: // specific EOI and rotate 2
+ case 0xE3: // specific EOI and rotate 3
+ case 0xE4: // specific EOI and rotate 4
+ case 0xE5: // specific EOI and rotate 5
+ case 0xE6: // specific EOI and rotate 6
+ case 0xE7: // specific EOI and rotate 7
+ BX_PIC_THIS s.master_pic.isr &= ~(1 << (value-0xE0));
+ BX_PIC_THIS s.master_pic.lowest_priority = (value - 0xE0);
+ service_master_pic();
+
+ break;
+
+ default:
+ BX_PANIC(("write to port 20h = %02x", value));
+ } /* switch (value) */
+ break;
+
+ case 0x21:
+ /* initialization mode operation */
+ if (BX_PIC_THIS s.master_pic.init.in_init) {
+ switch (BX_PIC_THIS s.master_pic.init.byte_expected) {
+ case 2:
+ BX_PIC_THIS s.master_pic.interrupt_offset = value & 0xf8;
+ BX_PIC_THIS s.master_pic.init.byte_expected = 3;
+ BX_DEBUG(("master: init command 2 = %02x", (unsigned) value));
+ BX_DEBUG((" offset = INT %02x",
+ BX_PIC_THIS s.master_pic.interrupt_offset));
+ return;
+ break;
+ case 3:
+ BX_DEBUG(("master: init command 3 = %02x", (unsigned) value));
+ if (BX_PIC_THIS s.master_pic.init.requires_4) {
+ BX_PIC_THIS s.master_pic.init.byte_expected = 4;
+ }
+ else {
+ BX_PIC_THIS s.master_pic.init.in_init = 0;
+ }
+ return;
+ break;
+ case 4:
+ BX_DEBUG(("master: init command 4 = %02x", (unsigned) value));
+ if (value & 0x02) {
+ BX_DEBUG((" auto EOI"));
+ BX_PIC_THIS s.master_pic.auto_eoi = 1;
+ }
+ else {
+ BX_DEBUG(("normal EOI interrupt"));
+ BX_PIC_THIS s.master_pic.auto_eoi = 0;
+ }
+ if (value & 0x01) {
+ BX_DEBUG((" 80x86 mode"));
+ } else
+ BX_PANIC((" not 80x86 mode"));
+ BX_PIC_THIS s.master_pic.init.in_init = 0;
+ return;
+ break;
+ default:
+ BX_PANIC(("master expecting bad init command"));
+ }
+ }
+
+ /* normal operation */
+ BX_DEBUG(("setting master pic IMR to %02x", value));
+ BX_PIC_THIS s.master_pic.imr = value;
+ service_master_pic();
+ return;
+ break;
+
+ case 0xA0:
+ if (value & 0x10) { /* initialization command 1 */
+ BX_DEBUG(("slave: init command 1 found"));
+ BX_DEBUG((" requires 4 = %u",
+ (unsigned) (value & 0x01) ));
+ BX_DEBUG((" cascade mode: [0=cascade,1=single] %u",
+ (unsigned) ((value & 0x02) >> 1)));
+ BX_PIC_THIS s.slave_pic.init.in_init = 1;
+ BX_PIC_THIS s.slave_pic.init.requires_4 = (value & 0x01);
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 2; /* operation command 2 */
+ BX_PIC_THIS s.slave_pic.imr = 0x00; /* clear irq mask */
+ BX_PIC_THIS s.slave_pic.isr = 0x00; /* no IRQ's in service */
+ BX_PIC_THIS s.slave_pic.irr = 0x00; /* no IRQ's requested */
+ BX_PIC_THIS s.slave_pic.lowest_priority = 7;
+ BX_PIC_THIS s.slave_pic.INT = 0; /* reprogramming clears previous INTR request */
+ BX_PIC_THIS s.slave_pic.auto_eoi = 0;
+ BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = 0;
+ if (value & 0x02)
+ BX_PANIC(("slave: ICW1: single mode not supported"));
+ if (value & 0x08) {
+ BX_PANIC(("slave: ICW1: level sensitive mode not supported"));
+ }
+ else {
+ BX_DEBUG(("slave: ICW1: edge triggered mode selected"));
+ }
+ return;
+ }
+
+ if ( (value & 0x18) == 0x08 ) { /* OCW3 */
+ Bit8u special_mask, poll, read_op;
+
+ special_mask = (value & 0x60) >> 5;
+ poll = (value & 0x04) >> 2;
+ read_op = (value & 0x03);
+ if (poll) {
+ BX_PIC_THIS s.slave_pic.polled = 1;
+ return;
+ }
+ if (read_op == 0x02) /* read IRR */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 0;
+ else if (read_op == 0x03) /* read ISR */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 1;
+ if (special_mask == 0x02) { /* cancel special mask */
+ BX_PIC_THIS s.slave_pic.special_mask = 0;
+ }
+ else if (special_mask == 0x03) { /* set specific mask */
+ BX_PIC_THIS s.slave_pic.special_mask = 1;
+ service_slave_pic();
+ }
+ return;
+ }
+
+ switch (value) {
+ case 0x00: // Rotate in auto eoi mode clear
+ case 0x80: // Rotate in auto eoi mode set
+ BX_PIC_THIS s.slave_pic.rotate_on_autoeoi = (value != 0);
+ break;
+
+ case 0x0A: /* select read interrupt request register */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 0;
+ break;
+ case 0x0B: /* select read interrupt in-service register */
+ BX_PIC_THIS s.slave_pic.read_reg_select = 1;
+ break;
+
+ case 0xA0: // Rotate on non-specific end of interrupt
+ case 0x20: /* end of interrupt command */
+
+ clear_highest_interrupt(& BX_PIC_THIS s.slave_pic);
+
+ if(value == 0xA0) {// Rotate in Auto-EOI mode
+ BX_PIC_THIS s.slave_pic.lowest_priority ++;
+ if(BX_PIC_THIS s.slave_pic.lowest_priority > 7)
+ BX_PIC_THIS s.slave_pic.lowest_priority = 0;
+ }
+
+ service_slave_pic();
+ break;
+
+ case 0x40: // Intel PIC spec-sheet seems to indicate this should be ignored
+ BX_INFO(("IRQ no-op"));
+ break;
+
+ case 0x60: /* specific EOI 0 */
+ case 0x61: /* specific EOI 1 */
+ case 0x62: /* specific EOI 2 */
+ case 0x63: /* specific EOI 3 */
+ case 0x64: /* specific EOI 4 */
+ case 0x65: /* specific EOI 5 */
+ case 0x66: /* specific EOI 6 */
+ case 0x67: /* specific EOI 7 */
+ BX_PIC_THIS s.slave_pic.isr &= ~(1 << (value-0x60));
+ service_slave_pic();
+ break;
+
+ // IRQ lowest priority commands
+ case 0xC0: // 0 7 6 5 4 3 2 1
+ case 0xC1: // 1 0 7 6 5 4 3 2
+ case 0xC2: // 2 1 0 7 6 5 4 3
+ case 0xC3: // 3 2 1 0 7 6 5 4
+ case 0xC4: // 4 3 2 1 0 7 6 5
+ case 0xC5: // 5 4 3 2 1 0 7 6
+ case 0xC6: // 6 5 4 3 2 1 0 7
+ case 0xC7: // 7 6 5 4 3 2 1 0
+ BX_INFO(("IRQ lowest command 0x%x", value));
+ BX_PIC_THIS s.slave_pic.lowest_priority = value - 0xC0;
+ break;
+
+ case 0xE0: // specific EOI and rotate 0
+ case 0xE1: // specific EOI and rotate 1
+ case 0xE2: // specific EOI and rotate 2
+ case 0xE3: // specific EOI and rotate 3
+ case 0xE4: // specific EOI and rotate 4
+ case 0xE5: // specific EOI and rotate 5
+ case 0xE6: // specific EOI and rotate 6
+ case 0xE7: // specific EOI and rotate 7
+ BX_PIC_THIS s.slave_pic.isr &= ~(1 << (value-0xE0));
+ BX_PIC_THIS s.slave_pic.lowest_priority = (value - 0xE0);
+ service_slave_pic();
+
+ break;
+
+ default:
+ BX_PANIC(("write to port A0h = %02x", value));
+ } /* switch (value) */
+ break;
+
+ case 0xA1:
+ /* initialization mode operation */
+ if (BX_PIC_THIS s.slave_pic.init.in_init) {
+ switch (BX_PIC_THIS s.slave_pic.init.byte_expected) {
+ case 2:
+ BX_PIC_THIS s.slave_pic.interrupt_offset = value & 0xf8;
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 3;
+ BX_DEBUG(("slave: init command 2 = %02x", (unsigned) value));
+ BX_DEBUG((" offset = INT %02x",
+ BX_PIC_THIS s.slave_pic.interrupt_offset));
+ return;
+ break;
+ case 3:
+ BX_DEBUG(("slave: init command 3 = %02x", (unsigned) value));
+ if (BX_PIC_THIS s.slave_pic.init.requires_4) {
+ BX_PIC_THIS s.slave_pic.init.byte_expected = 4;
+ } else {
+ BX_PIC_THIS s.slave_pic.init.in_init = 0;
+ }
+ return;
+ break;
+ case 4:
+ BX_DEBUG(("slave: init command 4 = %02x", (unsigned) value));
+ if (value & 0x02) {
+ BX_DEBUG((" auto EOI"));
+ BX_PIC_THIS s.slave_pic.auto_eoi = 1;
+ }
+ else {
+ BX_DEBUG(("normal EOI interrupt"));
+ BX_PIC_THIS s.slave_pic.auto_eoi = 0;
+ }
+ if (value & 0x01) {
+ BX_DEBUG((" 80x86 mode"));
+ } else
+ BX_PANIC((" not 80x86 mode"));
+ BX_PIC_THIS s.slave_pic.init.in_init = 0;
+ return;
+ break;
+ default:
+ BX_PANIC(("slave: expecting bad init command"));
+ }
+ }
+
+ /* normal operation */
+ BX_DEBUG(("setting slave pic IMR to %02x", value));
+ BX_PIC_THIS s.slave_pic.imr = value;
+ service_slave_pic();
+ return;
+ break;
+ } /* switch (address) */
+
+ return;
+}
+
+// new IRQ signal handling routines
+
+ void
+bx_pic_c::lower_irq(unsigned irq_no)
+{
+#if BX_SUPPORT_APIC
+ // forward this function call to the ioapic too
+ if (DEV_ioapic_present())
+ bx_devices.ioapic->untrigger_irq (irq_no, -1);
+#endif
+
+ if ((irq_no <= 7) && (BX_PIC_THIS s.master_pic.IRQ_line[irq_no])) {
+ BX_DEBUG(("IRQ line %d now low", (unsigned) irq_no));
+ BX_PIC_THIS s.master_pic.IRQ_line[irq_no] = 0;
+ BX_PIC_THIS s.master_pic.irr &= ~(1 << irq_no);
+ if ((BX_PIC_THIS s.master_pic.irr & ~BX_PIC_THIS s.master_pic.imr) == 0) {
+ BX_SET_INTR(0);
+ BX_PIC_THIS s.master_pic.INT = 0;
+ }
+ } else if ((irq_no > 7) && (irq_no <= 15) &&
+ (BX_PIC_THIS s.slave_pic.IRQ_line[irq_no-8])) {
+ BX_DEBUG(("IRQ line %d now low", (unsigned) irq_no));
+ BX_PIC_THIS s.slave_pic.IRQ_line[irq_no - 8] = 0;
+ BX_PIC_THIS s.slave_pic.irr &= ~(1 << (irq_no - 8));
+ if ((BX_PIC_THIS s.slave_pic.irr & ~BX_PIC_THIS s.slave_pic.imr) == 0) {
+ BX_PIC_THIS s.slave_pic.INT = 0;
+ lower_irq(2);
+ }
+ }
+}
+
+ void
+bx_pic_c::raise_irq(unsigned irq_no)
+{
+#if BX_SUPPORT_APIC
+ // forward this function call to the ioapic too
+ bx_devices.ioapic->trigger_irq (irq_no, -1);
+#endif
+
+ if ((irq_no <= 7) && (!BX_PIC_THIS s.master_pic.IRQ_line[irq_no])) {
+ BX_DEBUG(("IRQ line %d now high", (unsigned) irq_no));
+ BX_PIC_THIS s.master_pic.IRQ_line[irq_no] = 1;
+ BX_PIC_THIS s.master_pic.irr |= (1 << irq_no);
+ service_master_pic();
+ } else if ((irq_no > 7) && (irq_no <= 15) &&
+ (!BX_PIC_THIS s.slave_pic.IRQ_line[irq_no-8])) {
+ BX_DEBUG(("IRQ line %d now high", (unsigned) irq_no));
+ BX_PIC_THIS s.slave_pic.IRQ_line[irq_no - 8] = 1;
+ BX_PIC_THIS s.slave_pic.irr |= (1 << (irq_no - 8));
+ service_slave_pic();
+ }
+}
+
+void bx_pic_c::clear_highest_interrupt(bx_pic_t *pic)
+{
+ int irq;
+ int lowest_priority;
+ int highest_priority;
+
+ /* clear highest current in service bit */
+ lowest_priority = pic->lowest_priority;
+ highest_priority = lowest_priority + 1;
+ if(highest_priority > 7)
+ highest_priority = 0;
+
+ irq = highest_priority;
+ do {
+ if (pic->isr & (1 << irq)) {
+ pic->isr &= ~(1 << irq);
+ break; /* Return mask of bit cleared. */
+ }
+
+ irq ++;
+ if(irq > 7)
+ irq = 0;
+ } while(irq != highest_priority);
+
+}
+
+ /* */
+ void
+bx_pic_c::service_master_pic(void)
+{
+ Bit8u unmasked_requests;
+ int irq;
+ Bit8u isr, max_irq;
+ Bit8u highest_priority = BX_PIC_THIS s.master_pic.lowest_priority + 1;
+ if(highest_priority > 7)
+ highest_priority = 0;
+
+ if (BX_PIC_THIS s.master_pic.INT) { /* last interrupt still not acknowleged */
+ return;
+ }
+
+ if (BX_PIC_THIS s.master_pic.special_mask) {
+ /* all priorities may be enabled. check all IRR bits except ones
+ * which have corresponding ISR bits set
+ */
+ max_irq = highest_priority;
+ }
+ else { /* normal mode */
+ /* Find the highest priority IRQ that is enabled due to current ISR */
+ isr = BX_PIC_THIS s.master_pic.isr;
+ if (isr) {
+ max_irq = highest_priority;
+ while ( (isr & (1 << max_irq)) == 0) {
+ max_irq++;
+ if(max_irq > 7)
+ max_irq = 0;
+ }
+ if (max_irq == highest_priority ) return; /* Highest priority interrupt in-service,
+ * no other priorities allowed */
+ if (max_irq > 7) BX_PANIC(("error in service_master_pic()"));
+ }
+ else
+ max_irq = highest_priority; /* 0..7 bits in ISR are cleared */
+ }
+
+
+ /* now, see if there are any higher priority requests */
+ if ((unmasked_requests = (BX_PIC_THIS s.master_pic.irr & ~BX_PIC_THIS s.master_pic.imr)) ) {
+ irq = highest_priority;
+ do {
+ /* for special mode, since we're looking at all IRQ's, skip if
+ * current IRQ is already in-service
+ */
+ if ( ! (BX_PIC_THIS s.master_pic.special_mask && ((BX_PIC_THIS s.master_pic.isr >> irq) & 0x01)) ) {
+ if (unmasked_requests & (1 << irq)) {
+ BX_DEBUG(("signalling IRQ(%u)", (unsigned) irq));
+ BX_PIC_THIS s.master_pic.INT = 1;
+ BX_SET_INTR(1);
+ BX_PIC_THIS s.master_pic.irq = irq;
+ return;
+ } /* if (unmasked_requests & ... */
+ }
+
+ irq ++;
+ if(irq > 7)
+ irq = 0;
+ } while(irq != max_irq); /* do ... */
+ } /* if (unmasked_requests = ... */
+}
+
+
+ void
+bx_pic_c::service_slave_pic(void)
+{
+ Bit8u unmasked_requests;
+ int irq;
+ Bit8u isr, max_irq;
+ Bit8u highest_priority = BX_PIC_THIS s.slave_pic.lowest_priority + 1;
+ if(highest_priority > 7)
+ highest_priority = 0;
+
+ if (BX_PIC_THIS s.slave_pic.INT) { /* last interrupt still not acknowleged */
+ return;
+ }
+
+ if (BX_PIC_THIS s.slave_pic.special_mask) {
+ /* all priorities may be enabled. check all IRR bits except ones
+ * which have corresponding ISR bits set
+ */
+ max_irq = highest_priority;
+ }
+ else { /* normal mode */
+ /* Find the highest priority IRQ that is enabled due to current ISR */
+ isr = BX_PIC_THIS s.slave_pic.isr;
+ if (isr) {
+ max_irq = highest_priority;
+ while ( (isr & (1 << max_irq)) == 0) {
+ max_irq++;
+ if(max_irq > 7)
+ max_irq = 0;
+ }
+ if (max_irq == highest_priority ) return; /* Highest priority interrupt in-service,
+ * no other priorities allowed */
+ if (max_irq > 7) BX_PANIC(("error in service_master_pic()"));
+ }
+ else
+ max_irq = highest_priority; /* 0..7 bits in ISR are cleared */
+ }
+
+
+ /* now, see if there are any higher priority requests */
+ if ((unmasked_requests = (BX_PIC_THIS s.slave_pic.irr & ~BX_PIC_THIS s.slave_pic.imr)) ) {
+ irq = highest_priority;
+ do {
+ /* for special mode, since we're looking at all IRQ's, skip if
+ * current IRQ is already in-service
+ */
+ if ( ! (BX_PIC_THIS s.slave_pic.special_mask && ((BX_PIC_THIS s.slave_pic.isr >> irq) & 0x01)) ) {
+ if (unmasked_requests & (1 << irq)) {
+ BX_DEBUG(("slave: signalling IRQ(%u)", (unsigned) 8 + irq));
+
+ BX_PIC_THIS s.slave_pic.INT = 1;
+ BX_PIC_THIS s.slave_pic.irq = irq;
+ BX_PIC_THIS raise_irq(2); /* request IRQ 2 on master pic */
+ return;
+ } /* if (unmasked_requests & ... */
+ }
+
+ irq ++;
+ if(irq > 7)
+ irq = 0;
+ } while(irq != max_irq); /* do ... */
+ } /* if (unmasked_requests = ... */
+}
+
+
+ /* CPU handshakes with PIC after acknowledging interrupt */
+ Bit8u
+bx_pic_c::IAC(void)
+{
+ Bit8u vector;
+ Bit8u irq;
+
+ BX_SET_INTR(0);
+ BX_PIC_THIS s.master_pic.INT = 0;
+ BX_PIC_THIS s.master_pic.irr &= ~(1 << BX_PIC_THIS s.master_pic.irq);
+ // In autoeoi mode don't set the isr bit.
+ if(!BX_PIC_THIS s.master_pic.auto_eoi)
+ BX_PIC_THIS s.master_pic.isr |= (1 << BX_PIC_THIS s.master_pic.irq);
+ else if(BX_PIC_THIS s.master_pic.rotate_on_autoeoi)
+ BX_PIC_THIS s.master_pic.lowest_priority = BX_PIC_THIS s.master_pic.irq;
+
+ if (BX_PIC_THIS s.master_pic.irq != 2) {
+ irq = BX_PIC_THIS s.master_pic.irq;
+ vector = irq + BX_PIC_THIS s.master_pic.interrupt_offset;
+ }
+ else { /* IRQ2 = slave pic IRQ8..15 */
+ BX_PIC_THIS s.slave_pic.INT = 0;
+ BX_PIC_THIS s.master_pic.IRQ_line[2] = 0;
+ irq = BX_PIC_THIS s.slave_pic.irq;
+ vector = irq + BX_PIC_THIS s.slave_pic.interrupt_offset;
+ BX_PIC_THIS s.slave_pic.irr &= ~(1 << BX_PIC_THIS s.slave_pic.irq);
+ // In autoeoi mode don't set the isr bit.
+ if(!BX_PIC_THIS s.slave_pic.auto_eoi)
+ BX_PIC_THIS s.slave_pic.isr |= (1 << BX_PIC_THIS s.slave_pic.irq);
+ else if(BX_PIC_THIS s.slave_pic.rotate_on_autoeoi)
+ BX_PIC_THIS s.slave_pic.lowest_priority = BX_PIC_THIS s.slave_pic.irq;
+ service_slave_pic();
+ irq += 8; // for debug printing purposes
+ }
+
+ service_master_pic();
+
+ BX_DBG_IAC_REPORT(vector, irq);
+ return(vector);
+}
+
+ Bit8u
+bx_pic_c::irq_to_vec(Bit8u irq)
+{
+ Bit8u vector = 0;
+
+ if (irq >= 8 && irq <= 15)
+ vector = irq + BX_PIC_THIS s.slave_pic.interrupt_offset;
+ else if (irq != 2 && irq <= 7)
+ vector = irq + BX_PIC_THIS s.master_pic.interrupt_offset;
+ else
+ BX_ERROR(("invalid irq!\n"));
+
+ return vector;
+}
+
+ void
+bx_pic_c::show_pic_state(void)
+{
+#if defined(BX_DEBUGGER) && (BX_DEBUGGER == 1)
+dbg_printf("s.master_pic.imr = %02x\n", BX_PIC_THIS s.master_pic.imr);
+dbg_printf("s.master_pic.isr = %02x\n", BX_PIC_THIS s.master_pic.isr);
+dbg_printf("s.master_pic.irr = %02x\n", BX_PIC_THIS s.master_pic.irr);
+dbg_printf("s.master_pic.irq = %02x\n", BX_PIC_THIS s.master_pic.irq);
+dbg_printf("s.slave_pic.imr = %02x\n", BX_PIC_THIS s.slave_pic.imr);
+dbg_printf("s.slave_pic.isr = %02x\n", BX_PIC_THIS s.slave_pic.isr);
+dbg_printf("s.slave_pic.irr = %02x\n", BX_PIC_THIS s.slave_pic.irr);
+dbg_printf("s.slave_pic.irq = %02x\n", BX_PIC_THIS s.slave_pic.irq);
+#endif
+}
diff --git a/tools/ioemu/iodev/pic.h b/tools/ioemu/iodev/pic.h
new file mode 100644
index 0000000000..378809d67d
--- /dev/null
+++ b/tools/ioemu/iodev/pic.h
@@ -0,0 +1,97 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pic.h,v 1.11 2003/08/04 16:03:09 akrisak Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#if BX_USE_PIC_SMF
+# define BX_PIC_SMF static
+# define BX_PIC_THIS thePic->
+#else
+# define BX_PIC_SMF
+# define BX_PIC_THIS this->
+#endif
+
+
+
+typedef struct {
+ Bit8u single_PIC; /* 0=cascaded PIC, 1=master only */
+ Bit8u interrupt_offset; /* programmable interrupt vector offset */
+ union {
+ Bit8u slave_connect_mask; /* for master, a bit for each interrupt line
+ 0=not connect to a slave, 1=connected */
+ Bit8u slave_id; /* for slave, id number of slave PIC */
+ } u;
+ Bit8u sfnm; /* specially fully nested mode: 0=no, 1=yes*/
+ Bit8u buffered_mode; /* 0=no buffered mode, 1=buffered mode */
+ Bit8u master_slave; /* master/slave: 0=slave PIC, 1=master PIC */
+ Bit8u auto_eoi; /* 0=manual EOI, 1=automatic EOI */
+ Bit8u imr; /* interrupt mask register, 1=masked */
+ Bit8u isr; /* in service register */
+ Bit8u irr; /* interrupt request register */
+ Bit8u read_reg_select; /* 0=IRR, 1=ISR */
+ Bit8u irq; /* current IRQ number */
+ Bit8u lowest_priority; /* current lowest priority irq */
+ bx_bool INT; /* INT request pin of PIC */
+ bx_bool IRQ_line[8]; /* IRQ pins of PIC */
+ struct {
+ bx_bool in_init;
+ bx_bool requires_4;
+ int byte_expected;
+ } init;
+ bx_bool special_mask;
+ bx_bool polled; /* Set when poll command is issued. */
+ bx_bool rotate_on_autoeoi; /* Set when should rotate in auto-eoi mode. */
+ } bx_pic_t;
+
+
+class bx_pic_c : public bx_pic_stub_c {
+public:
+ bx_pic_c(void);
+ ~bx_pic_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+ virtual void lower_irq(unsigned irq_no);
+ virtual void raise_irq(unsigned irq_no);
+ virtual Bit8u IAC(void);
+ virtual void show_pic_state(void);
+ Bit8u irq_to_vec(Bit8u);
+
+private:
+ struct {
+ bx_pic_t master_pic;
+ bx_pic_t slave_pic;
+ } s;
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_PIC_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+
+ BX_PIC_SMF void service_master_pic(void);
+ BX_PIC_SMF void service_slave_pic(void);
+ BX_PIC_SMF void clear_highest_interrupt(bx_pic_t *pic);
+ };
diff --git a/tools/ioemu/iodev/pit.cc b/tools/ioemu/iodev/pit.cc
new file mode 100644
index 0000000000..cf4777a759
--- /dev/null
+++ b/tools/ioemu/iodev/pit.cc
@@ -0,0 +1,856 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pit.cc,v 1.15 2003/07/31 12:04:48 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+#include "bochs.h"
+
+#if (BX_USE_NEW_PIT==0)
+
+#define LOG_THIS bx_pit.
+
+
+// NOTES ON THE 8253/8254 PIT MODES
+
+// MODE 0: Interrupt on Terminal Count
+// ===================================
+// Writing new count action:
+// loaded upon next CLK pulse. counting doesn't start until GATE=1
+// GATE 0..1 transition:
+// ???
+// GATE 1..0 transition:
+// counter expiration action:
+// wraps to FFFF
+// * OUT rises until new count val or new control word for mode 0 written
+
+// MODE 1: Programmable Monoflop
+// =============================
+// Writing new count action:
+// not effective for current process
+// GATE 0..1 transition:
+// loads counter
+// counter expiration action:
+// wraps to FFFF
+// NOTES:
+// OUT rises until new count val or new control word for mode 0 written
+
+// MODE 2: Rate Generator
+// ======================
+// Writing new count action:
+// ???
+// GATE 0..1 transition:
+// loads initial count val and starts counting
+// counter expiration action:
+// reloads after count expires
+// NOTES:
+// * after control word & initial count val N loaded, PIT starts
+// counting upon next CLK pulse.
+// * when counter reaches 1, OUT drops to a low level, for one
+// CLK cycle. (short peak pulse generated)
+// * afterwards, the initial count val is automatically reloaded
+// and the PIT restarts the same counting operation again.
+// * distance of two OUT pulses is N CLK cycles long.
+// * GATE=1 enables, GATE=0 disables counter.
+// * if GATE drops to low level during counting operation and rises
+// to high level later, PIT loads initial count value at the
+// rise and starts counting.
+// * PIT starts counting after last data byte written if GATE=1
+// * if the output is low when the gate goes low, the output is
+// immediately set high.
+
+// MODE 3: Square Wave Generator
+// =============================
+// Writing new count action:
+// ???
+// GATE 0..1 transition:
+// ???
+// counter expiration action:
+// reloads after count expires
+// NOTES:
+// * initially OUT at a high level
+// * drop of GATE to a low level while OUT low, raises OUT to a high level
+// * a rise from a low to a high level at GATE (trigger pulse),
+// loads the counter with the initial count value and starts
+// counting operation
+// * a new count value supplied during the course of an active
+// counting operation doesn't affect the current process.
+// At the end of the current half cycle, the PIT loads the new value
+// * if the GATE line goes low, count is temporarily halted until GATE
+// returns high
+// * if the OUT line is high when GATE goes low, OUT is forced low.
+// ??? different for odd/even counts
+
+// MODE 4: Software Triggered Pulse
+// ================================
+// Writing new count action:
+// ???
+// GATE 0..1 transition:
+// ???
+// counter expiration action:
+// wraps to FFFF
+// NOTES:
+
+// MODE 5: Hardware Triggered Pulse
+// ================================
+// Writing new count action:
+// ???
+// GATE 0..1 transition:
+// ???
+// counter expiration action:
+// wraps to FFFF
+// NOTES:
+
+
+
+#define BX_PIT_LATCH_MODE_LSB 10
+#define BX_PIT_LATCH_MODE_MSB 11
+#define BX_PIT_LATCH_MODE_16BIT 12
+
+
+bx_pit_c bx_pit;
+#if BX_USE_PIT_SMF
+#define this (&bx_pit)
+#endif
+
+#ifdef OUT
+# undef OUT
+#endif
+
+
+bx_pit_c::bx_pit_c( void )
+{
+ put("PIT");
+ settype(PITLOG);
+ memset(&s, 0, sizeof(s));
+
+ /* 8254 PIT (Programmable Interval Timer) */
+
+ BX_PIT_THIS s.timer_handle[1] = BX_NULL_TIMER_HANDLE;
+ BX_PIT_THIS s.timer_handle[2] = BX_NULL_TIMER_HANDLE;
+}
+
+bx_pit_c::~bx_pit_c( void )
+{
+}
+
+
+ int
+bx_pit_c::init( void )
+{
+ DEV_register_irq(0, "8254 PIT");
+ DEV_register_ioread_handler(this, read_handler, 0x0040, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0041, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0042, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0043, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0061, "8254 PIT", 1);
+
+ DEV_register_iowrite_handler(this, write_handler, 0x0040, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0041, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0042, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0043, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0061, "8254 PIT", 1);
+
+ BX_PIT_THIS s.speaker_data_on = 0;
+ BX_PIT_THIS s.refresh_clock_div2 = 0;
+
+ BX_PIT_THIS s.timer[0].mode = 3; /* periodic rate generator */
+ BX_PIT_THIS s.timer[0].latch_mode = BX_PIT_LATCH_MODE_16BIT;
+ BX_PIT_THIS s.timer[0].input_latch_value = 0;
+ BX_PIT_THIS s.timer[0].input_latch_toggle = 0;
+ BX_PIT_THIS s.timer[0].output_latch_value = 0;
+ BX_PIT_THIS s.timer[0].output_latch_toggle = 0;
+ BX_PIT_THIS s.timer[0].output_latch_full = 0;
+ BX_PIT_THIS s.timer[0].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */
+ BX_PIT_THIS s.timer[0].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */
+ BX_PIT_THIS s.timer[0].bcd_mode = 0; /* binary counting mode */
+ BX_PIT_THIS s.timer[0].GATE = 1; /* GATE tied to + logic */
+ BX_PIT_THIS s.timer[0].OUT = 1;
+ BX_PIT_THIS s.timer[0].active = 0;
+
+ BX_PIT_THIS s.timer[1].mode = 3; /* periodic rate generator */
+ BX_PIT_THIS s.timer[1].latch_mode = BX_PIT_LATCH_MODE_16BIT;
+ BX_PIT_THIS s.timer[1].input_latch_value = 0;
+ BX_PIT_THIS s.timer[1].input_latch_toggle = 0;
+ BX_PIT_THIS s.timer[1].output_latch_value = 0;
+ BX_PIT_THIS s.timer[1].output_latch_toggle = 0;
+ BX_PIT_THIS s.timer[1].output_latch_full = 0;
+ BX_PIT_THIS s.timer[1].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */
+ BX_PIT_THIS s.timer[1].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */
+ BX_PIT_THIS s.timer[1].bcd_mode = 0; /* binary counting mode */
+ BX_PIT_THIS s.timer[1].GATE = 1; /* GATE tied to + logic */
+ BX_PIT_THIS s.timer[1].OUT = 1;
+ BX_PIT_THIS s.timer[1].active = 0;
+
+ BX_PIT_THIS s.timer[2].mode = 3; /* periodic rate generator */
+ BX_PIT_THIS s.timer[2].latch_mode = BX_PIT_LATCH_MODE_16BIT;
+ BX_PIT_THIS s.timer[2].input_latch_value = 0;
+ BX_PIT_THIS s.timer[2].input_latch_toggle = 0;
+ BX_PIT_THIS s.timer[2].output_latch_value = 0;
+ BX_PIT_THIS s.timer[2].output_latch_toggle = 0;
+ BX_PIT_THIS s.timer[2].output_latch_full = 0;
+ BX_PIT_THIS s.timer[2].counter_max = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */
+ BX_PIT_THIS s.timer[2].counter = 0; /* 0xFFFF + 1 : (1193182 / 65535 = 18.2Hz) */
+ BX_PIT_THIS s.timer[2].bcd_mode = 0; /* binary counting mode */
+ BX_PIT_THIS s.timer[2].GATE = 0; /* timer2 gate controlled by port 61h bit 0 */
+ BX_PIT_THIS s.timer[2].OUT = 1;
+ BX_PIT_THIS s.timer[2].active = 0;
+
+ return(1);
+}
+
+void bx_pit_c::reset(unsigned type) {
+}
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pit_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_PIT_SMF
+ bx_pit_c *class_ptr = (bx_pit_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_pit_c::read( Bit32u address, unsigned int io_len )
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIT_SMF
+ if (bx_dbg.pit)
+ BX_INFO(("pit: io read from port %04x", (unsigned) address));
+
+ switch (address) {
+ case 0x40: /* timer 0 - system ticks */
+ return( read_counter(0) );
+ break;
+
+ case 0x42: /* timer 2 read */
+ return( read_counter(2) );
+ break;
+
+ case 0x61:
+ /* AT, port 61h */
+ BX_PIT_THIS s.refresh_clock_div2 = !BX_PIT_THIS s.refresh_clock_div2;
+ return( (BX_PIT_THIS s.timer[2].OUT<<5) |
+ (BX_PIT_THIS s.refresh_clock_div2<<4) |
+ (BX_PIT_THIS s.speaker_data_on<<1) |
+ (BX_PIT_THIS s.timer[2].GATE) );
+ break;
+
+ default:
+ BX_PANIC(("pit: unsupported io read from port %04x", address));
+ }
+ return(0); /* keep compiler happy */
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pit_c::write_handler(void *this_ptr, Bit32u address, Bit32u dvalue, unsigned io_len)
+{
+#if !BX_USE_PIT_SMF
+ bx_pit_c *class_ptr = (bx_pit_c *) this_ptr;
+
+ class_ptr->write(address, dvalue, io_len);
+}
+
+ void
+bx_pit_c::write( Bit32u address, Bit32u dvalue,
+ unsigned int io_len )
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIT_SMF
+ Bit8u command, mode, bcd_mode;
+ Bit8u value;
+
+ value = (Bit8u ) dvalue;
+
+ if (bx_dbg.pit)
+ BX_INFO(("pit: write to port %04x = %02x",
+ (unsigned) address, (unsigned) value));
+
+ switch (address) {
+ case 0x40: /* timer 0: write count register */
+ write_count_reg( value, 0 );
+ break;
+
+ case 0x41: /* timer 1: write count register */
+ write_count_reg( value, 1 );
+ break;
+
+ case 0x42: /* timer 2: write count register */
+ write_count_reg( value, 2 );
+ break;
+
+ case 0x43: /* timer 0-2 mode control */
+ /* |7 6 5 4|3 2 1|0|
+ * |-------|-----|-|
+ * |command|mode |bcd/binary|
+ */
+ command = value >> 4;
+ mode = (value >> 1) & 0x07;
+ bcd_mode = value & 0x01;
+#if 0
+BX_INFO(("timer 0-2 mode control: comm:%02x mode:%02x bcd_mode:%u",
+ (unsigned) command, (unsigned) mode, (unsigned) bcd_mode));
+#endif
+
+ if ( (mode > 5) || (command > 0x0e) )
+ BX_PANIC(("pit: outp(43h)=%02xh out of range", (unsigned) value));
+ if (bcd_mode)
+ BX_PANIC(("pit: outp(43h)=%02xh: bcd mode unhandled",
+ (unsigned) bcd_mode));
+
+ switch (command) {
+ case 0x0: /* timer 0: counter latch */
+ latch( 0 );
+ break;
+
+ case 0x1: /* timer 0: LSB mode */
+ case 0x2: /* timer 0: MSB mode */
+ BX_PANIC(("pit: outp(43h): command %02xh unhandled",
+ (unsigned) command));
+ break;
+ case 0x3: /* timer 0: 16-bit mode */
+ BX_PIT_THIS s.timer[0].mode = mode;
+ BX_PIT_THIS s.timer[0].latch_mode = BX_PIT_LATCH_MODE_16BIT;
+ BX_PIT_THIS s.timer[0].input_latch_value = 0;
+ BX_PIT_THIS s.timer[0].input_latch_toggle = 0;
+ BX_PIT_THIS s.timer[0].bcd_mode = bcd_mode;
+ if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 )
+ BX_PANIC(("pit: outp(43h): comm 3, mode %02x, bcd %02x unhandled",
+ (unsigned) mode, bcd_mode));
+ break;
+ case 0x4: /* timer 1: counter latch */
+ latch( 1 );
+ break;
+
+ case 0x5: /* timer 1: LSB mode */
+ case 0x6: /* timer 1: MSB mode */
+ BX_INFO(("pit: outp(43h): command %02xh unhandled (ignored)",
+ (unsigned) command));
+ break;
+ case 0x7: /* timer 1: 16-bit mode */
+ BX_PIT_THIS s.timer[1].mode = mode;
+ BX_PIT_THIS s.timer[1].latch_mode = BX_PIT_LATCH_MODE_16BIT;
+ BX_PIT_THIS s.timer[1].input_latch_value = 0;
+ BX_PIT_THIS s.timer[1].input_latch_toggle = 0;
+ BX_PIT_THIS s.timer[1].bcd_mode = bcd_mode;
+ if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 )
+ BX_PANIC(("pit: outp(43h): comm 7, mode %02x, bcd %02x unhandled",
+ (unsigned) mode, bcd_mode));
+ break;
+ case 0x8: /* timer 2: counter latch */
+ latch( 2 );
+ break;
+
+ case 0x9: /* timer 2: LSB mode */
+ case 0xa: /* timer 2: MSB mode */
+ BX_PANIC(("pit: outp(43h): command %02xh unhandled",
+ (unsigned) command));
+ break;
+ case 0xb: /* timer 2: 16-bit mode */
+ BX_PIT_THIS s.timer[2].mode = mode;
+ BX_PIT_THIS s.timer[2].latch_mode = BX_PIT_LATCH_MODE_16BIT;
+ BX_PIT_THIS s.timer[2].input_latch_value = 0;
+ BX_PIT_THIS s.timer[2].input_latch_toggle = 0;
+ BX_PIT_THIS s.timer[2].bcd_mode = bcd_mode;
+ if ( (mode!=3 && mode!=2 && mode!=0) || bcd_mode!=0 )
+ BX_PANIC(("pit: outp(43h): comm Bh, mode %02x, bcd %02x unhandled",
+ (unsigned) mode, bcd_mode));
+ break;
+#if 0
+ case 0xd: /* general counter latch */
+ if (value & 0x08) /* select counter 2 */
+ latch( 2 );
+ if (value & 0x04) /* select counter 1 */
+ latch( 1 );
+ if (value & 0x02) /* select counter 0 */
+ latch( 0 );
+ break;
+
+ case 0xe: /* latch status of timers */
+ BX_PANIC(("pit: outp(43h): command %02xh unhandled",
+ (unsigned) command);
+ break;
+#endif
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ BX_INFO(("pit: ignoring 8254 command %u", (unsigned) command));
+ break;
+
+ default: /* 0xc & 0xf */
+ BX_PANIC(("pit: outp(43h) command %1xh unhandled",
+ (unsigned) command));
+ break;
+ }
+ break;
+
+ case 0x61:
+ BX_PIT_THIS s.speaker_data_on = (value >> 1) & 0x01;
+/*??? only on AT+ */
+ set_GATE(2, value & 0x01);
+#if BX_CPU_LEVEL < 2
+ /* ??? XT: */
+ bx_kbd_port61h_write(value);
+#endif
+ break;
+
+ default:
+ BX_PANIC(("pit: unsupported io write to port %04x = %02x",
+ (unsigned) address, (unsigned) value));
+ }
+}
+
+
+
+
+ void
+bx_pit_c::write_count_reg( Bit8u value, unsigned timerid )
+{
+ bx_bool xfer_complete;
+
+ switch ( BX_PIT_THIS s.timer[timerid].latch_mode ) {
+ case BX_PIT_LATCH_MODE_16BIT: /* write1=LSB, write2=MSB */
+ if (BX_PIT_THIS s.timer[timerid].input_latch_toggle==0) {
+ BX_PIT_THIS s.timer[timerid].input_latch_value = value;
+ BX_PIT_THIS s.timer[timerid].input_latch_toggle = 1;
+ xfer_complete = 0;
+ if (bx_dbg.pit)
+ BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write L = %02x", (unsigned) value));
+ }
+ else {
+ BX_PIT_THIS s.timer[timerid].input_latch_value |= (value << 8);
+ BX_PIT_THIS s.timer[timerid].input_latch_toggle = 0;
+ xfer_complete = 1;
+ if (bx_dbg.pit)
+ BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write H = %02x", (unsigned) value));
+ }
+ break;
+
+ case BX_PIT_LATCH_MODE_MSB: /* write1=MSB, LSB=0 */
+ BX_PIT_THIS s.timer[timerid].input_latch_value = (value << 8);
+ xfer_complete = 1;
+ if (bx_dbg.pit)
+ BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write H = %02x", (unsigned) value));
+ break;
+
+ case BX_PIT_LATCH_MODE_LSB: /* write1=LSB, MSB=0 */
+ BX_PIT_THIS s.timer[timerid].input_latch_value = value;
+ xfer_complete = 1;
+ if (bx_dbg.pit)
+ BX_INFO(("pit: BX_PIT_THIS s.timer[timerid] write L = %02x", (unsigned) value));
+ break;
+
+ default:
+ BX_PANIC(("write_count_reg: latch_mode unknown"));
+ xfer_complete = 0;
+ }
+
+ if (xfer_complete) {
+ BX_PIT_THIS s.timer[timerid].counter_max = BX_PIT_THIS s.timer[timerid].input_latch_value;
+
+ // reprogramming counter clears latch
+ BX_PIT_THIS s.timer[timerid].output_latch_full = 0;
+
+ // counter bounds
+ // mode minimum maximum
+ // 0 1 0
+ // 1 1 0
+ // 2 2 0
+ // 3 2 0
+ // 4 1 0
+ // 5 1 0
+ switch (BX_PIT_THIS s.timer[timerid].mode) {
+ case 0:
+ BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max;
+ BX_PIT_THIS s.timer[timerid].active = 1;
+ if (BX_PIT_THIS s.timer[timerid].GATE) {
+ BX_PIT_THIS s.timer[timerid].OUT = 0; // OUT pin starts low
+ start( timerid );
+ }
+ break;
+ case 1:
+ BX_PANIC(("pit:write_count_reg(%u): mode1 unsupported",
+ timerid));
+ break;
+ case 2:
+ if ( BX_PIT_THIS s.timer[timerid].counter_max == 1 )
+ BX_PANIC(("pit:write_count_reg(%u): mode %u counter_max=1",
+ timerid, (unsigned) BX_PIT_THIS s.timer[timerid].mode));
+ if ( BX_PIT_THIS s.timer[timerid].GATE && !BX_PIT_THIS s.timer[timerid].active ) {
+ // software triggered
+ BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max;
+ BX_PIT_THIS s.timer[timerid].active = 1;
+ BX_PIT_THIS s.timer[timerid].OUT = 1; // initially set high
+ start( timerid );
+ }
+ break;
+ case 3:
+ if ( BX_PIT_THIS s.timer[timerid].counter_max == 1 )
+ BX_PANIC(("pit:write_count_reg(%u): mode %u counter_max=1",
+ timerid, (unsigned) BX_PIT_THIS s.timer[timerid].mode));
+ BX_PIT_THIS s.timer[timerid].counter_max = BX_PIT_THIS s.timer[timerid].counter_max & 0xfffe;
+ if ( BX_PIT_THIS s.timer[timerid].GATE && !BX_PIT_THIS s.timer[timerid].active ) {
+ // software triggered
+ BX_PIT_THIS s.timer[timerid].counter = BX_PIT_THIS s.timer[timerid].counter_max;
+ BX_PIT_THIS s.timer[timerid].active = 1;
+ BX_PIT_THIS s.timer[timerid].OUT = 1; // initially set high
+ start( timerid );
+ }
+ break;
+ case 4:
+ BX_PANIC(("pit:write_count_reg(%u): mode4 unsupported",
+ timerid));
+ break;
+ case 5:
+ BX_PANIC(("pit:write_count_reg(%u): mode5 unsupported",
+ timerid));
+ break;
+ }
+ }
+}
+
+
+ Bit8u
+bx_pit_c::read_counter( unsigned timerid )
+{
+ Bit16u counter_value;
+ Bit8u retval;
+
+ if (BX_PIT_THIS s.timer[timerid].output_latch_full) { /* latched read */
+ counter_value = BX_PIT_THIS s.timer[timerid].output_latch_value;
+ }
+ else { /* direct unlatched read */
+ counter_value = BX_PIT_THIS s.timer[timerid].counter;
+BX_INFO(("CV=%04x", (unsigned) BX_PIT_THIS s.timer[timerid].counter));
+ }
+
+ switch (BX_PIT_THIS s.timer[timerid].latch_mode) {
+ case BX_PIT_LATCH_MODE_LSB:
+ retval = (Bit8u ) counter_value;
+ BX_PIT_THIS s.timer[timerid].output_latch_full = 0;
+ break;
+ case BX_PIT_LATCH_MODE_MSB:
+ retval = (Bit8u ) ( counter_value >> 8 );
+ BX_PIT_THIS s.timer[timerid].output_latch_full = 0;
+ break;
+ case BX_PIT_LATCH_MODE_16BIT:
+ if (BX_PIT_THIS s.timer[timerid].output_latch_toggle==0) { /* LSB 1st */
+ retval = (Bit8u ) counter_value;
+ }
+ else { /* MSB 2nd */
+ retval = (Bit8u ) ( counter_value >> 8 );
+ }
+ BX_PIT_THIS s.timer[timerid].output_latch_toggle = !BX_PIT_THIS s.timer[timerid].output_latch_toggle;
+ if (BX_PIT_THIS s.timer[timerid].output_latch_toggle == 0)
+ BX_PIT_THIS s.timer[timerid].output_latch_full = 0;
+ break;
+ default:
+ BX_PANIC(("pit: io read from port 40h: unknown latch mode"));
+ retval = 0; /* keep compiler happy */
+ }
+ return( retval );
+}
+
+
+ void
+bx_pit_c::latch( unsigned timerid )
+{
+ /* subsequent counter latch commands are ignored until value read out */
+ if (BX_PIT_THIS s.timer[timerid].output_latch_full) {
+ BX_INFO(("pit: pit(%u) latch: output latch full, ignoring",
+ timerid));
+ return;
+ }
+
+ BX_PIT_THIS s.timer[timerid].output_latch_value = BX_PIT_THIS s.timer[timerid].counter;
+
+ if (bx_dbg.pit)
+ BX_INFO(("pit: latch_value = %u", (unsigned) BX_PIT_THIS s.timer[timerid].output_latch_value));
+ BX_PIT_THIS s.timer[timerid].output_latch_toggle = 0;
+ BX_PIT_THIS s.timer[timerid].output_latch_full = 1;
+}
+
+ void
+bx_pit_c::set_GATE(unsigned pit_id, unsigned value)
+{
+ // GATE's for Timer 0 & Timer 1 are tied high.
+ if (pit_id != 2)
+ BX_PANIC(("pit:set_GATE: pit_id != 2"));
+
+ value = (value > 0);
+
+ /* if no transition of GATE input line, then nothing to do */
+ if (value == BX_PIT_THIS s.timer[2].GATE)
+ return;
+
+ if (value) { /* PIT2: GATE transition from 0 to 1 */
+ BX_PIT_THIS s.timer[2].GATE = 1;
+ switch ( BX_PIT_THIS s.timer[2].mode ) {
+ case 0:
+ BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max;
+ if (BX_PIT_THIS s.timer[2].active) {
+ BX_PIT_THIS s.timer[2].OUT = 0;
+ }
+ start( 2 );
+ break;
+ case 2:
+ // begin counting, reload counter
+ BX_PIT_THIS s.timer[2].active = 1;
+ BX_PIT_THIS s.timer[2].OUT = 1;
+ BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max;
+ start( 2 );
+ break;
+ case 3:
+ // begin counting, reload counter
+ BX_PIT_THIS s.timer[2].active = 1;
+ BX_PIT_THIS s.timer[2].OUT = 1;
+ BX_PIT_THIS s.timer[2].counter = BX_PIT_THIS s.timer[2].counter_max;
+ start( 2 );
+ break;
+ case 1:
+ case 4:
+ case 5:
+ default:
+ BX_PANIC(("bx_pit_c::set_GATE: unhandled timer2 mode %u",
+ (unsigned) BX_PIT_THIS s.timer[2].mode));
+ }
+ }
+ else { // PIT2: GATE transition from 1 to 0, deactivate
+ BX_PIT_THIS s.timer[2].GATE = 0;
+ switch ( BX_PIT_THIS s.timer[2].mode ) {
+ case 0:
+ break;
+ case 2:
+ // 1) stops count, 2) OUT goes immediately high
+ BX_PIT_THIS s.timer[2].active = 0;
+ BX_PIT_THIS s.timer[2].OUT = 1;
+ break;
+ case 3:
+ // 1) stops count, 2) OUT goes immediately high
+ BX_PIT_THIS s.timer[2].active = 0;
+ BX_PIT_THIS s.timer[2].OUT = 1;
+ break;
+ case 1:
+ case 4:
+ case 5:
+ default:
+ BX_PANIC(("bx_pit_c::set_GATE: unhandled timer2 mode %u",
+ (unsigned) BX_PIT_THIS s.timer[2].mode));
+ }
+ }
+}
+
+
+ void
+bx_pit_c::start(unsigned timerid)
+{
+ unsigned long period_hz;
+
+ if (BX_PIT_THIS s.timer[timerid].counter_max == 0x0000) {
+ period_hz = 1193182 / 65536;
+ }
+ else {
+ period_hz = 1193182 / BX_PIT_THIS s.timer[timerid].counter_max;
+ }
+ BX_INFO(("timer%u period set to %lu hz", timerid, period_hz));
+
+
+ switch (BX_PIT_THIS s.timer[timerid].mode) {
+ case 0: /* single timeout */
+ break;
+ case 1: /* retriggerable one-shot */
+ BX_PANIC(("start: mode %u unhandled",
+ (unsigned) BX_PIT_THIS s.timer[timerid].mode));
+ break;
+ case 2: /* rate generator */
+ break;
+ case 3: /* square wave mode */
+ break;
+ case 4: /* software triggered strobe */
+ BX_PANIC(("start: mode %u unhandled",
+ (unsigned) BX_PIT_THIS s.timer[timerid].mode));
+ break;
+ case 5: /* hardware retriggerable strobe */
+ BX_PANIC(("start: mode %u unhandled",
+ (unsigned) BX_PIT_THIS s.timer[timerid].mode));
+ break;
+ default:
+ BX_PANIC(("start: timer%u has bad mode",
+ (unsigned) BX_PIT_THIS s.timer[timerid].mode));
+ }
+}
+
+
+
+
+ int
+bx_pit_c::SaveState( class state_file *fd )
+{
+ fd->write_check ("8254 start");
+ fd->write (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s));
+ fd->write_check ("8254 end");
+ return(0);
+}
+
+
+ int
+bx_pit_c::LoadState( class state_file *fd )
+{
+ fd->read_check ("8254 start");
+ fd->read (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s));
+ fd->read_check ("8254 end");
+ return(0);
+}
+
+
+#if 0
+ void
+bx_kbd_port61h_write(Bit8u value)
+{
+// PcError("KBD_PORT61H_WRITE(): not implemented yet");
+ UNUSED( value );
+}
+#endif
+
+
+ bx_bool
+bx_pit_c::periodic( Bit32u usec_delta )
+{
+ bx_bool prev_timer0_out;
+
+ prev_timer0_out = BX_PIT_THIS s.timer[0].OUT;
+
+ for (unsigned i = 0; i < 3; i++) {
+ // is timer enabled and active?
+ if ( BX_PIT_THIS s.timer[i].GATE && BX_PIT_THIS s.timer[i].active ) {
+ switch ( BX_PIT_THIS s.timer[i].mode ) {
+ case 0: // Mode 0: Single Timeout
+ // wraps after count expires
+ if ( BX_PIT_THIS s.timer[i].counter == 0 ) {
+ // counter previously expired, wrap counter
+ BX_PIT_THIS s.timer[i].counter = 0xffff;
+ }
+ else if ( usec_delta >= BX_PIT_THIS s.timer[i].counter ) {
+ // counter expired
+ BX_PIT_THIS s.timer[i].counter = 0;
+ BX_PIT_THIS s.timer[i].OUT = 1;
+ }
+ else {
+ // decrement counter by elapsed useconds
+ BX_PIT_THIS s.timer[i].counter -= (Bit16u ) usec_delta;
+ }
+ break;
+
+ case 1: // Mode 1: Retriggerable One-Shot
+ // wraps after count expires
+ BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u",
+ i, (unsigned) BX_PIT_THIS s.timer[i].mode));
+ break;
+
+ case 2: // Mode 2: Rate Generator
+ // reloads after count expires
+ // OUT is low when counter=1, high otherwise
+ // min count=2, max count=0
+ if ( BX_PIT_THIS s.timer[i].counter == 0 ) {
+ // max counter val, just wrap
+ BX_PIT_THIS s.timer[i].counter = 0xffff;
+ BX_PIT_THIS s.timer[i].OUT = 1;
+ }
+ else if ( BX_PIT_THIS s.timer[i].counter == 1 ) {
+ // counter previously expired, reload
+ BX_PIT_THIS s.timer[i].counter = BX_PIT_THIS s.timer[i].counter_max;
+ BX_PIT_THIS s.timer[i].OUT = 1;
+ }
+ else if ( (BX_PIT_THIS s.timer[i].counter == 2) ||
+ (usec_delta >= (Bit32u(BX_PIT_THIS s.timer[i].counter) - 1)) ) {
+ // in either case, counter will reach 1
+ BX_PIT_THIS s.timer[i].counter = 1;
+ BX_PIT_THIS s.timer[i].OUT = 0;
+ }
+ else {
+ // decrement counter by elapsed useconds
+ BX_PIT_THIS s.timer[i].counter -= (Bit16u ) usec_delta;
+ }
+ break;
+
+ case 3: // Mode 3: Square Wave Mode
+ // reloads after count expires
+ // min count=2, max count=0
+ if ( BX_PIT_THIS s.timer[i].counter == 0 ) {
+ // max count, dec by 2
+ BX_PIT_THIS s.timer[i].counter = 0xfffe;
+ }
+ else if ( (BX_PIT_THIS s.timer[i].counter <= 2) ||
+ ( (usec_delta*2) >= BX_PIT_THIS s.timer[i].counter ) ) {
+ // counter expired, reload
+ BX_PIT_THIS s.timer[i].counter = BX_PIT_THIS s.timer[i].counter_max;
+ BX_PIT_THIS s.timer[i].OUT = !BX_PIT_THIS s.timer[i].OUT;
+ //BX_INFO(("CV: reload t%u to %04x", (unsigned) i, (unsigned)
+ // BX_PIT_THIS s.timer[i].counter));
+ }
+ else {
+ // decrement counter by elapsed useconds
+ BX_PIT_THIS s.timer[i].counter -= (Bit16u ) ( 2*usec_delta );
+ //BX_INFO(("CV: dec count to %04x",
+ // (unsigned) BX_PIT_THIS s.timer[i].counter));
+ }
+ break;
+
+ case 4: // Mode 4: Software Triggered Strobe
+ // wraps after count expires
+ BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u",
+ i, (unsigned) BX_PIT_THIS s.timer[i].mode));
+ break;
+
+ case 5: // Mode 5: Hardware Retriggerable Strobe
+ // wraps after count expires
+ BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u",
+ i, (unsigned) BX_PIT_THIS s.timer[i].mode));
+ break;
+ default:
+ BX_PANIC(("bx_pit_c::periodic: bad mode: timer[%u], mode %u",
+ i, (unsigned) BX_PIT_THIS s.timer[i].mode));
+ break;
+ } // switch ( BX_PIT_THIS s.tim...
+ } // if ( BX_PIT_THIS s.timer[i]...
+ } // for (unsigned i...
+
+ // see if there's a rising edge on timer0's output to trigger an IRQ0.
+ if ( (prev_timer0_out==0) && (BX_PIT_THIS s.timer[0].OUT==1) )
+ return(1); // request IRQ 0
+ else
+ return(0);
+}
+
+#endif // #if (BX_USE_NEW_PIT==0)
diff --git a/tools/ioemu/iodev/pit.h b/tools/ioemu/iodev/pit.h
new file mode 100644
index 0000000000..49b663bf9d
--- /dev/null
+++ b/tools/ioemu/iodev/pit.h
@@ -0,0 +1,103 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pit.h,v 1.10 2002/10/25 11:44:40 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#ifndef _BX_PIT_H
+#define _BX_PIT_H
+
+#include "config.h"
+
+#if (BX_USE_NEW_PIT==0)
+
+#if BX_USE_PIT_SMF
+# define BX_PIT_SMF static
+# define BX_PIT_THIS bx_pit.
+#else
+# define BX_PIT_SMF
+# define BX_PIT_THIS this->
+#endif
+
+#ifdef OUT
+# undef OUT
+#endif
+
+
+typedef struct {
+ Bit8u mode;
+ Bit8u latch_mode;
+ Bit16u input_latch_value;
+ bx_bool input_latch_toggle;
+ Bit16u output_latch_value;
+ bx_bool output_latch_toggle;
+ bx_bool output_latch_full;
+ Bit16u counter_max;
+ Bit16u counter;
+ bx_bool bcd_mode;
+ bx_bool active;
+ bx_bool GATE; // GATE input pin
+ bx_bool OUT; // OUT output pin
+ } bx_pit_t;
+
+
+
+
+class bx_pit_c : public logfunctions {
+public:
+ bx_pit_c( void );
+ ~bx_pit_c( void );
+ BX_PIT_SMF int init(void);
+ BX_PIT_SMF void reset( unsigned type);
+ BX_PIT_SMF bx_bool periodic( Bit32u usec_delta );
+
+ BX_PIT_SMF int SaveState( class state_file *fd );
+ BX_PIT_SMF int LoadState( class state_file *fd );
+
+private:
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_PIT_SMF
+ Bit32u read( Bit32u addr, unsigned int len );
+ void write( Bit32u addr, Bit32u Value, unsigned int len );
+#endif
+
+ struct s_type {
+ bx_pit_t timer[3];
+ Bit8u speaker_data_on;
+ bx_bool refresh_clock_div2;
+ int timer_handle[3];
+ } s;
+
+ BX_PIT_SMF void write_count_reg( Bit8u value, unsigned timerid );
+ BX_PIT_SMF Bit8u read_counter( unsigned timerid );
+ BX_PIT_SMF void latch( unsigned timerid );
+ BX_PIT_SMF void set_GATE(unsigned pit_id, unsigned value);
+ BX_PIT_SMF void start(unsigned timerid);
+ };
+
+extern bx_pit_c bx_pit;
+
+#endif // #if (BX_USE_NEW_PIT==0)
+#endif // #ifndef _BX_PIT_H
diff --git a/tools/ioemu/iodev/pit82c54.cc b/tools/ioemu/iodev/pit82c54.cc
new file mode 100644
index 0000000000..493faf6ee1
--- /dev/null
+++ b/tools/ioemu/iodev/pit82c54.cc
@@ -0,0 +1,930 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pit82c54.cc,v 1.23 2003/06/29 17:24:52 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+/*
+ * Emulator of an Intel 8254/82C54 Programmable Interval Timer.
+ * Greg Alexander <yakovlev@usa.com>
+ *
+ *
+ * Things I am unclear on (greg):
+ * 1.)What happens if both the status and count registers are latched,
+ * but the first of the two count registers has already been read?
+ * I.E.:
+ * latch count 0 (16-bit)
+ * Read count 0 (read LSByte)
+ * READ_BACK status of count 0
+ * Read count 0 - do you get MSByte or status?
+ * This will be flagged as an error.
+ * 2.)What happens when we latch the output in the middle of a 2-part
+ * unlatched read?
+ * 3.)I assumed that programming a counter removes a latched status.
+ * 4.)I implemented the 8254 description of mode 0, not the 82C54 one.
+ * 5.)clock() calls represent a rising clock edge followed by a falling
+ * clock edge.
+ * 6.)What happens when we trigger mode 1 in the middle of a 2-part
+ * write?
+ */
+
+#include "bochs.h"
+#include "pit82c54.h"
+#define LOG_THIS this->
+
+
+void pit_82C54::print_counter(counter_type & thisctr) {
+#if 1
+ BX_INFO(("Printing Counter"));
+ BX_INFO(("count: %d",thisctr.count));
+ BX_INFO(("count_binary: %x",thisctr.count_binary));
+ BX_INFO(("counter gate: %x",thisctr.GATE));
+ BX_INFO(("counter OUT: %x",thisctr.OUTpin));
+ BX_INFO(("next_change_time: %d",thisctr.next_change_time));
+ BX_INFO(("End Counter Printout"));
+#endif
+}
+
+void pit_82C54::print_cnum(Bit8u cnum) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Bad counter index to print_cnum"));
+ } else {
+ print_counter(counter[cnum]);
+ }
+}
+
+ void pit_82C54::latch_counter(counter_type & thisctr) {
+ if(thisctr.count_LSB_latched || thisctr.count_MSB_latched) {
+ //Do nothing because previous latch has not been read.;
+ } else {
+ switch(thisctr.read_state) {
+ case MSByte:
+ thisctr.outlatch=thisctr.count & 0xFFFF;
+ thisctr.count_MSB_latched=1;
+ break;
+ case LSByte:
+ thisctr.outlatch=thisctr.count & 0xFFFF;
+ thisctr.count_LSB_latched=1;
+ break;
+ case LSByte_multiple:
+ thisctr.outlatch=thisctr.count & 0xFFFF;
+ thisctr.count_LSB_latched=1;
+ thisctr.count_MSB_latched=1;
+ break;
+ case MSByte_multiple:
+ if(!(seen_problems & UNL_2P_READ)) {
+// seen_problems|=UNL_2P_READ;
+ BX_ERROR(("Unknown behavior when latching during 2-part read."));
+ BX_ERROR((" This message will not be repeated."));
+ }
+ //I guess latching and resetting to LSB first makes sense;
+ BX_DEBUG(("Setting read_state to LSB_mult"));
+ thisctr.read_state=LSByte_multiple;
+ thisctr.outlatch=thisctr.count & 0xFFFF;
+ thisctr.count_LSB_latched=1;
+ thisctr.count_MSB_latched=1;
+ break;
+ default:
+ BX_ERROR(("Unknown read mode found during latch command."));
+ break;
+ }
+ }
+ }
+
+ void pit_82C54::set_OUT (counter_type & thisctr, bool data) {
+ //This will probably have a callback, so I put it here.
+ thisctr.OUTpin=data;
+ }
+
+ void BX_CPP_AttrRegparmN(2)
+pit_82C54::set_count (counter_type & thisctr, Bit32u data) {
+ thisctr.count=data & 0xFFFF;
+ set_binary_to_count(thisctr);
+ }
+
+ void BX_CPP_AttrRegparmN(1)
+pit_82C54::set_count_to_binary(counter_type & thisctr) {
+ if(thisctr.bcd_mode) {
+ thisctr.count=
+ (((thisctr.count_binary/1)%10)<<0) |
+ (((thisctr.count_binary/10)%10)<<4) |
+ (((thisctr.count_binary/100)%10)<<8) |
+ (((thisctr.count_binary/1000)%10)<<12)
+ ;
+ } else {
+ thisctr.count=thisctr.count_binary;
+ }
+ }
+
+ void BX_CPP_AttrRegparmN(1)
+pit_82C54::set_binary_to_count(counter_type & thisctr) {
+ if(thisctr.bcd_mode) {
+ thisctr.count_binary=
+ (1*((thisctr.count>>0)&0xF)) +
+ (10*((thisctr.count>>4)&0xF)) +
+ (100*((thisctr.count>>8)&0xF)) +
+ (1000*((thisctr.count>>12)&0xF))
+ ;
+ } else {
+ thisctr.count_binary=thisctr.count;
+ }
+ }
+
+ void BX_CPP_AttrRegparmN(1)
+pit_82C54::decrement (counter_type & thisctr) {
+ if(!thisctr.count) {
+ if(thisctr.bcd_mode) {
+ thisctr.count=0x9999;
+ thisctr.count_binary=9999;
+ } else {
+ thisctr.count=0xFFFF;
+ thisctr.count_binary=0xFFFF;
+ }
+ } else {
+ thisctr.count_binary--;
+ set_count_to_binary(thisctr);
+ }
+ }
+
+ void pit_82C54::init (void) {
+ Bit8u i;
+
+ put("PIT81");
+ settype(PIT81LOG);
+
+ for(i=0;i<3;i++) {
+ BX_DEBUG(("Setting read_state to LSB"));
+ counter[i].read_state=LSByte;
+ counter[i].write_state=LSByte;
+ counter[i].GATE=1;
+ counter[i].OUTpin=1;
+ counter[i].triggerGATE=0;
+ counter[i].mode=4;
+ counter[i].first_pass=0;
+ counter[i].bcd_mode=0;
+ counter[i].count=0;
+ counter[i].count_binary=0;
+ counter[i].state_bit_1=0;
+ counter[i].state_bit_2=0;
+ counter[i].null_count=0;
+ counter[i].rw_mode=1;
+ counter[i].count_written=1;
+ counter[i].count_LSB_latched=0;
+ counter[i].count_MSB_latched=0;
+ counter[i].status_latched=0;
+ counter[i].next_change_time=0;
+ }
+ seen_problems=0;
+ }
+
+ pit_82C54::pit_82C54 (void) {
+ init();
+ }
+
+ void pit_82C54::reset (unsigned type) {
+ }
+
+void BX_CPP_AttrRegparmN(2)
+pit_82C54::decrement_multiple(counter_type & thisctr, Bit32u cycles) {
+ while(cycles>0) {
+ if(cycles<=thisctr.count_binary) {
+ thisctr.count_binary-=cycles;
+ cycles-=cycles;
+ set_count_to_binary(thisctr);
+ } else {
+ cycles-=(thisctr.count_binary+1);
+ thisctr.count_binary-=thisctr.count_binary;
+ set_count_to_binary(thisctr);
+ decrement(thisctr);
+ }
+ }
+}
+
+void pit_82C54::clock_multiple(Bit8u cnum, Bit32u cycles) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Counter number too high in clock"));
+ } else {
+ counter_type & thisctr = counter[cnum];
+ while(cycles>0) {
+ if(thisctr.next_change_time==0) {
+ if(thisctr.count_written) {
+ switch(thisctr.mode) {
+ case 0:
+ if(thisctr.GATE && (thisctr.write_state!=MSByte_multiple)) {
+ decrement_multiple(thisctr, cycles);
+ }
+ break;
+ case 1:
+ decrement_multiple(thisctr, cycles);
+ break;
+ case 2:
+ if( (!thisctr.first_pass) && thisctr.GATE ) {
+ decrement_multiple(thisctr, cycles);
+ }
+ break;
+ case 3:
+ if( (!thisctr.first_pass) && thisctr.GATE ) {
+ decrement_multiple(thisctr, 2*cycles);
+ }
+ break;
+ case 4:
+ if(thisctr.GATE) {
+ decrement_multiple(thisctr, cycles);
+ }
+ break;
+ case 5:
+ decrement_multiple(thisctr, cycles);
+ break;
+ default:
+ break;
+ }
+ }
+ cycles-=cycles;
+ } else {
+ switch(thisctr.mode) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ if( thisctr.next_change_time > cycles ) {
+ decrement_multiple(thisctr,cycles);
+ thisctr.next_change_time-=cycles;
+ cycles-=cycles;
+ } else {
+ decrement_multiple(thisctr,(thisctr.next_change_time-1));
+ cycles-=thisctr.next_change_time;
+ clock(cnum);
+ }
+ break;
+ case 3:
+ if( thisctr.next_change_time > cycles ) {
+ decrement_multiple(thisctr,cycles*2);
+ thisctr.next_change_time-=cycles;
+ cycles-=cycles;
+ } else {
+ decrement_multiple(thisctr,(thisctr.next_change_time-1)*2);
+ cycles-=thisctr.next_change_time;
+ clock(cnum);
+ }
+ break;
+ default:
+ cycles-=cycles;
+ break;
+ }
+ }
+ }
+#if 0
+ print_counter(thisctr);
+#endif
+ }
+}
+
+ void BX_CPP_AttrRegparmN(1)
+pit_82C54::clock(Bit8u cnum) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Counter number too high in clock"));
+ } else {
+ counter_type & thisctr = counter[cnum];
+ switch(thisctr.mode) {
+ case 0:
+ if(thisctr.count_written) {
+ if(thisctr.null_count) {
+ set_count(thisctr, thisctr.inlatch);
+ if(thisctr.GATE) {
+ if(thisctr.count_binary==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ thisctr.null_count=0;
+ } else {
+ if(thisctr.GATE && (thisctr.write_state!=MSByte_multiple)) {
+ decrement(thisctr);
+ if(!thisctr.OUTpin) {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ if(!thisctr.count) {
+ set_OUT(thisctr,1);
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ } else {
+ thisctr.next_change_time=0; //if the clock isn't moving.
+ }
+ }
+ } else {
+ thisctr.next_change_time=0; //default to 0.
+ }
+ thisctr.triggerGATE=0;
+ break;
+ case 1:
+ if(thisctr.count_written) {
+ if(thisctr.triggerGATE) {
+ set_count(thisctr, thisctr.inlatch);
+ if(thisctr.count_binary==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ }
+ thisctr.null_count=0;
+ set_OUT(thisctr,0);
+ if(thisctr.write_state==MSByte_multiple) {
+ BX_ERROR(("Undefined behavior when loading a half loaded count."));
+ }
+ } else {
+ decrement(thisctr);
+ if(!thisctr.OUTpin) {
+ if(thisctr.count_binary==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ }
+ if(thisctr.count==0) {
+ set_OUT(thisctr,1);
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ } else {
+ thisctr.next_change_time=0; //default to 0.
+ }
+ thisctr.triggerGATE=0;
+ break;
+ case 2:
+ if(thisctr.count_written) {
+ if(thisctr.triggerGATE || thisctr.first_pass) {
+ set_count(thisctr, thisctr.inlatch);
+ thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF;
+ thisctr.null_count=0;
+ if(thisctr.inlatch==1) {
+ BX_ERROR(("ERROR: count of 1 is invalid in pit mode 2."));
+ }
+ if(!thisctr.OUTpin) {
+ set_OUT(thisctr,1);
+ }
+ if(thisctr.write_state==MSByte_multiple) {
+ BX_ERROR(("Undefined behavior when loading a half loaded count."));
+ }
+ thisctr.first_pass=0;
+ } else {
+ if(thisctr.GATE) {
+ decrement(thisctr);
+ thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF;
+ if(thisctr.count==1) {
+ thisctr.next_change_time=1;
+ set_OUT(thisctr,0);
+ thisctr.first_pass=1;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ thisctr.triggerGATE=0;
+ break;
+ case 3:
+ if(thisctr.count_written) {
+ if( (thisctr.triggerGATE || thisctr.first_pass
+ || thisctr.state_bit_2) && thisctr.GATE ) {
+ set_count(thisctr, thisctr.inlatch & 0xFFFE);
+ thisctr.state_bit_1=thisctr.inlatch & 0x1;
+ if( (!thisctr.OUTpin) || (!(thisctr.state_bit_1))) {
+ if(((thisctr.count_binary/2)-1)==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=((thisctr.count_binary/2)-1) & 0xFFFF;
+ }
+ } else {
+ if((thisctr.count_binary/2)==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=(thisctr.count_binary/2) & 0xFFFF;
+ }
+ }
+ thisctr.null_count=0;
+ if(thisctr.inlatch==1) {
+ BX_ERROR(("Count of 1 is invalid in pit mode 3."));
+ }
+ if(!thisctr.OUTpin) {
+ set_OUT(thisctr,1);
+ } else if(thisctr.OUTpin && !thisctr.first_pass) {
+ set_OUT(thisctr,0);
+ }
+ if(thisctr.write_state==MSByte_multiple) {
+ BX_ERROR(("Undefined behavior when loading a half loaded count."));
+ }
+ thisctr.state_bit_2=0;
+ thisctr.first_pass=0;
+ } else {
+ if(thisctr.GATE) {
+ decrement(thisctr);
+ decrement(thisctr);
+ if( (!thisctr.OUTpin) || (!(thisctr.state_bit_1))) {
+ thisctr.next_change_time=((thisctr.count_binary/2)-1) & 0xFFFF;
+ } else {
+ thisctr.next_change_time=(thisctr.count_binary/2) & 0xFFFF;
+ }
+ if(thisctr.count==0) {
+ thisctr.state_bit_2=1;
+ thisctr.next_change_time=1;
+ }
+ if( (thisctr.count==2) &&
+ ( (!thisctr.OUTpin) || (!(thisctr.state_bit_1)))
+ ) {
+ thisctr.state_bit_2=1;
+ thisctr.next_change_time=1;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ thisctr.triggerGATE=0;
+ break;
+ case 4:
+ if(thisctr.count_written) {
+ if(!thisctr.OUTpin) {
+ set_OUT(thisctr,1);
+ }
+ if(thisctr.null_count) {
+ set_count(thisctr, thisctr.inlatch);
+ if(thisctr.GATE) {
+ if(thisctr.count_binary==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ thisctr.null_count=0;
+ if(thisctr.write_state==MSByte_multiple) {
+ BX_ERROR(("Undefined behavior when loading a half loaded count."));
+ }
+ thisctr.first_pass=1;
+ } else {
+ if(thisctr.GATE) {
+ decrement(thisctr);
+ if(thisctr.first_pass) {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ if(!thisctr.count) {
+ set_OUT(thisctr,0);
+ thisctr.next_change_time=1;
+ thisctr.first_pass=0;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ thisctr.triggerGATE=0;
+ break;
+ case 5:
+ if(thisctr.count_written) {
+ if(!thisctr.OUTpin) {
+ set_OUT(thisctr,1);
+ }
+ if(thisctr.triggerGATE) {
+ set_count(thisctr, thisctr.inlatch);
+ if(thisctr.count_binary==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ }
+ thisctr.null_count=0;
+ if(thisctr.write_state==MSByte_multiple) {
+ BX_ERROR(("Undefined behavior when loading a half loaded count."));
+ }
+ thisctr.first_pass=1;
+ } else {
+ decrement(thisctr);
+ if(thisctr.first_pass) {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ if(!thisctr.count) {
+ set_OUT(thisctr,0);
+ thisctr.next_change_time=1;
+ thisctr.first_pass=0;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ thisctr.triggerGATE=0;
+ break;
+ default:
+ BX_ERROR(("Mode not implemented."));
+ thisctr.next_change_time=0;
+ thisctr.triggerGATE=0;
+ break;
+ }
+ }
+ }
+
+ void pit_82C54::clock_all(Bit32u cycles) {
+ BX_DEBUG(("clock_all: cycles=%d",cycles));
+ clock_multiple(0,cycles);
+ clock_multiple(1,cycles);
+ clock_multiple(2,cycles);
+ }
+
+ Bit8u pit_82C54::read(Bit8u address) {
+ if(address>MAX_ADDRESS) {
+ BX_ERROR(("Counter address incorrect in data read."));
+ } else if(address==CONTROL_ADDRESS) {
+ BX_DEBUG(("PIT Read: Control Word Register."));
+ //Read from control word register;
+ /* This might be okay. If so, 0 seems the most logical
+ * return value from looking at the docs.
+ */
+ BX_ERROR(("Read from control word register not defined."));
+ return 0;
+ } else {
+ //Read from a counter;
+ BX_DEBUG(("PIT Read: Counter %d.",address));
+ counter_type & thisctr=counter[address];
+ if(thisctr.status_latched) {
+ //Latched Status Read;
+ if(thisctr.count_MSB_latched &&
+ (thisctr.read_state==MSByte_multiple) ) {
+ BX_ERROR(("Undefined output when status latched and count half read."));
+ } else {
+ thisctr.status_latched=0;
+ return thisctr.status_latch;
+ }
+ } else {
+ //Latched Count Read;
+ if(thisctr.count_LSB_latched) {
+ //Read Least Significant Byte;
+ if(thisctr.read_state==LSByte_multiple) {
+ BX_DEBUG(("Setting read_state to MSB_mult"));
+ thisctr.read_state=MSByte_multiple;
+ }
+ thisctr.count_LSB_latched=0;
+ return (thisctr.outlatch & 0xFF);
+ } else if(thisctr.count_MSB_latched) {
+ //Read Most Significant Byte;
+ if(thisctr.read_state==MSByte_multiple) {
+ BX_DEBUG(("Setting read_state to LSB_mult"));
+ thisctr.read_state=LSByte_multiple;
+ }
+ thisctr.count_MSB_latched=0;
+ return ((thisctr.outlatch>>8) & 0xFF);
+ } else {
+ //Unlatched Count Read;
+ if(!(thisctr.read_state & 0x1)) {
+ //Read Least Significant Byte;
+ if(thisctr.read_state==LSByte_multiple) {
+ thisctr.read_state=MSByte_multiple;
+ BX_DEBUG(("Setting read_state to MSB_mult"));
+ }
+ return (thisctr.count & 0xFF);
+ } else {
+ //Read Most Significant Byte;
+ if(thisctr.read_state==MSByte_multiple) {
+ BX_DEBUG(("Setting read_state to LSB_mult"));
+ thisctr.read_state=LSByte_multiple;
+ }
+ return ((thisctr.count>>8) & 0xFF);
+ }
+ }
+ }
+ }
+ //Should only get here on errors;
+ return 0;
+ }
+
+#ifdef BX_VMX_PIT
+//extra operations when use vmx pit device model
+ void pit_82C54::write_initcount_vmx(Bit8u cnum) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Counter number incorrect\n"));
+ }
+
+ ioreq_t *req = &((vcpu_iodata_t *) shared_page)->vp_ioreq;
+ extern bx_pic_c *thePic;
+ counter_type & thisctr = counter[cnum];
+ if(req->pdata_valid) {
+ BX_ERROR(("VMX_PIT:err!pit is port io!\n"));
+ }
+
+ if (thisctr.mode == 2) {//periodic mode, need HV to help send interrupt
+ req->state = STATE_IORESP_HOOK;
+
+// req->u.data = thisctr.inlatch * 1000 / PIT_FREQ;//init count:16 bit
+ req->u.data = thisctr.inlatch; //init count:16 bit
+ //get the pit irq(0)'s vector from pic DM
+ req->u.data |= ((thePic->irq_to_vec(0)) << 16 ); //timer vec:8 bit
+ req->u.data |= (cnum << 24); //PIT channel(0~2):2 bit
+ req->u.data |= ((thisctr.rw_mode) << 26); //rw mode:2 bit
+
+ BX_INFO(("VMX_PIT:whole pit hook packet = 0x%llx \n", (req->u.data ) ));
+ BX_INFO(("VMX_PIT:init counter = %d ms\n", (req->u.data & 0xFFFF) ));
+ }
+
+ }
+#endif
+
+ void pit_82C54::write(Bit8u address, Bit8u data) {
+ if(address>MAX_ADDRESS) {
+ BX_ERROR(("Counter address incorrect in data write."));
+ } else if(address==CONTROL_ADDRESS) {
+ Bit8u SC, RW, M, BCD;
+ controlword=data;
+ BX_DEBUG(("Control Word Write."));
+ SC = (controlword>>6) & 0x3;
+ RW = (controlword>>4) & 0x3;
+ M = (controlword>>1) & 0x7;
+ BCD = controlword & 0x1;
+ if(SC == 3) {
+ //READ_BACK command;
+ int i;
+ BX_DEBUG(("READ_BACK command."));
+ for(i=0;i<=MAX_COUNTER;i++) {
+ if((M>>i) & 0x1) {
+ //If we are using this counter;
+ counter_type & thisctr=counter[i];
+ if(!((controlword>>5) & 1)) {
+ //Latch Count;
+ latch_counter(thisctr);
+ }
+ if(!((controlword>>4) & 1)) {
+ //Latch Status;
+ if(thisctr.status_latched) {
+ //Do nothing because latched status has not been read.;
+ } else {
+ thisctr.status_latch=
+ ((thisctr.OUTpin & 0x1) << 7) |
+ ((thisctr.null_count & 0x1) << 6) |
+ ((thisctr.rw_mode & 0x3) << 4) |
+ ((thisctr.mode & 0x7) << 1) |
+ (thisctr.bcd_mode&0x1)
+ ;
+ thisctr.status_latched=1;
+ }
+ }
+ }
+ }
+ } else {
+ counter_type & thisctr = counter[SC];
+ if(!RW) {
+ //Counter Latch command;
+ BX_DEBUG(("Counter Latch command. SC=%d",SC));
+ latch_counter(thisctr);
+ } else {
+ //Counter Program Command;
+ BX_DEBUG(("Counter Program command. SC=%d, RW=%d, M=%d, BCD=%d",SC,RW,M,BCD));
+ thisctr.null_count=1;
+ thisctr.count_LSB_latched=0;
+ thisctr.count_MSB_latched=0;
+ thisctr.status_latched=0;
+ thisctr.inlatch=0;
+ thisctr.count_written=0;
+ thisctr.first_pass=1;
+ thisctr.rw_mode=RW;
+ thisctr.bcd_mode=(BCD > 0);
+ thisctr.mode=M;
+ switch(RW) {
+ case 0x1:
+ BX_DEBUG(("Setting read_state to LSB"));
+ thisctr.read_state=LSByte;
+ thisctr.write_state=LSByte;
+ break;
+ case 0x2:
+ BX_DEBUG(("Setting read_state to MSB"));
+ thisctr.read_state=MSByte;
+ thisctr.write_state=MSByte;
+ break;
+ case 0x3:
+ BX_DEBUG(("Setting read_state to LSB_mult"));
+ thisctr.read_state=LSByte_multiple;
+ thisctr.write_state=LSByte_multiple;
+ break;
+ default:
+ BX_ERROR(("RW field invalid in control word write."));
+ break;
+ }
+ //All modes except mode 0 have initial output of 1.;
+ if(M) {
+ set_OUT(thisctr, 1);
+ } else {
+ set_OUT(thisctr, 0);
+ }
+ thisctr.next_change_time=0;
+ }
+ }
+ } else {
+ //Write to counter initial value.
+ counter_type & thisctr = counter[address];
+ BX_DEBUG(("Write Initial Count: counter=%d, count=%d",address,data));
+ switch(thisctr.write_state) {
+ case LSByte_multiple:
+ thisctr.inlatch=(thisctr.inlatch & (0xFF<<8)) | data;
+ thisctr.write_state=MSByte_multiple;
+ break;
+ case LSByte:
+ thisctr.inlatch=(thisctr.inlatch & (0xFF<<8)) | data;
+ thisctr.null_count=1;
+ thisctr.count_written=1;
+#ifdef BX_VMX_PIT
+ write_initcount_vmx(address);
+#endif
+ break;
+ case MSByte_multiple:
+ thisctr.write_state=LSByte_multiple;
+ case MSByte: //shared between MSB_multiple and MSByte
+ thisctr.inlatch=(thisctr.inlatch & 0xFF) | (data<<8);
+ thisctr.null_count=1;
+ thisctr.count_written=1;
+#ifdef BX_VMX_PIT
+ write_initcount_vmx(address);
+#endif
+ break;
+ default:
+ BX_ERROR(("write counter in invalid write state."));
+ break;
+ }
+ switch(thisctr.mode) {
+ case 0:
+ if(thisctr.write_state==MSByte_multiple) {
+ set_OUT(thisctr,0);
+ }
+ thisctr.next_change_time=1;
+ break;
+ case 1:
+ if(thisctr.triggerGATE) { //for initial writes, if already saw trigger.
+ thisctr.next_change_time=1;
+ } //Otherwise, no change.
+ break;
+ case 6:
+ case 2:
+ thisctr.next_change_time=1; //FIXME: this could be loosened.
+ break;
+ case 7:
+ case 3:
+ thisctr.next_change_time=1; //FIXME: this could be loosened.
+ break;
+ case 4:
+ thisctr.next_change_time=1;
+ break;
+ case 5:
+ if(thisctr.triggerGATE) { //for initial writes, if already saw trigger.
+ thisctr.next_change_time=1;
+ } //Otherwise, no change.
+ break;
+ }
+ }
+ }
+
+ void pit_82C54::set_GATE(Bit8u cnum, bool data) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Counter number incorrect in 82C54 set_GATE"));
+ } else {
+ counter_type & thisctr = counter[cnum];
+ if(!( (thisctr.GATE&&data) || (!(thisctr.GATE||data)) )) {
+ BX_INFO(("Changing GATE %d to: %d",cnum,data));
+ thisctr.GATE=data;
+ if(thisctr.GATE) {
+ thisctr.triggerGATE=1;
+ }
+ switch(thisctr.mode) {
+ case 0:
+ if(data && thisctr.count_written) {
+ if(thisctr.null_count) {
+ thisctr.next_change_time=1;
+ } else {
+ if( (!thisctr.OUTpin) &&
+ (thisctr.write_state!=MSByte_multiple)
+ ) {
+ if(thisctr.count_binary==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ } else {
+ if(thisctr.null_count) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ break;
+ case 1:
+ if(data && thisctr.count_written) { //only triggers cause a change.
+ thisctr.next_change_time=1;
+ }
+ break;
+ case 2:
+ if(!data) {
+ set_OUT(thisctr,1);
+ thisctr.next_change_time=0;
+ } else {
+ if(thisctr.count_written) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ break;
+ case 3:
+ if(!data) {
+ set_OUT(thisctr,1);
+ thisctr.first_pass=1;
+ thisctr.next_change_time=0;
+ } else {
+ if(thisctr.count_written) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ break;
+ case 4:
+ if(!thisctr.OUTpin || thisctr.null_count) {
+ thisctr.next_change_time=1;
+ } else {
+ if(data && thisctr.count_written) {
+ if(thisctr.first_pass) {
+ if(thisctr.count_binary==0) {
+ thisctr.next_change_time=1;
+ } else {
+ thisctr.next_change_time=thisctr.count_binary & 0xFFFF;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ } else {
+ thisctr.next_change_time=0;
+ }
+ }
+ break;
+ case 5:
+ if(data && thisctr.count_written) { //only triggers cause a change.
+ thisctr.next_change_time=1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ bool pit_82C54::read_OUT(Bit8u cnum) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Counter number incorrect in 82C54 read_OUT"));
+ return 0;
+ } else {
+ return counter[cnum].OUTpin;
+ }
+ }
+
+ bool pit_82C54::read_GATE(Bit8u cnum) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Counter number incorrect in 82C54 read_GATE"));
+ return 0;
+ } else {
+ return counter[cnum].GATE;
+ }
+ }
+
+Bit32u pit_82C54::get_clock_event_time(Bit8u cnum) {
+ if(cnum>MAX_COUNTER) {
+ BX_ERROR(("Counter number incorrect in 82C54 read_GATE"));
+ return 0;
+ } else {
+ return counter[cnum].next_change_time;
+ }
+}
+
+Bit32u pit_82C54::get_next_event_time(void) {
+ Bit32u out;
+ Bit32u time0=get_clock_event_time(0);
+ Bit32u time1=get_clock_event_time(1);
+ Bit32u time2=get_clock_event_time(2);
+
+ out=time0;
+ if(time1 && (time1<out))
+ out=time1;
+ if(time2 && (time2<out))
+ out=time2;
+ return out;
+}
diff --git a/tools/ioemu/iodev/pit82c54.h b/tools/ioemu/iodev/pit82c54.h
new file mode 100644
index 0000000000..95d36b3973
--- /dev/null
+++ b/tools/ioemu/iodev/pit82c54.h
@@ -0,0 +1,143 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pit82c54.h,v 1.12 2003/03/02 23:59:11 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+/*
+ * Emulator of an Intel 8254/82C54 Programmable Interval Timer.
+ * Greg Alexander <yakovlev@usa.com>
+ *
+ * This code is not yet linked into Bochs, but has been included so
+ * that you can experiment with it. (bbd)
+ */
+
+#ifndef _PIT_82C54_H_
+#define _PIT_82C54_H_ 1
+
+#include "bochs.h"
+
+#ifdef BX_USE_VMX
+#define BX_VMX_PIT 1
+#define PIT_FREQ 1193181
+#endif
+
+
+class pit_82C54 : public logfunctions {
+
+public:
+ //Please do not use these. They are public because they have to be
+ // to compile on some platforms. They are not to be used by other
+ // classes.
+
+ enum rw_status {
+ LSByte=0,
+ MSByte=1,
+ LSByte_multiple=2,
+ MSByte_multiple=3
+ };
+
+private:
+
+ enum {
+ MAX_COUNTER=2,
+ MAX_ADDRESS=3,
+ CONTROL_ADDRESS=3,
+ MAX_MODE=5
+ };
+
+ enum real_RW_status {
+ LSB_real=1,
+ MSB_real=2,
+ BOTH_real=3
+ };
+
+ enum problem_type {
+ UNL_2P_READ=1
+ };
+
+ struct counter_type {
+ //Chip IOs;
+ bool GATE; //GATE Input value at end of cycle
+ bool OUTpin; //OUT output this cycle
+
+ //Architected state;
+ Bit32u count; //Counter value this cycle
+ Bit16u outlatch; //Output latch this cycle
+ Bit16u inlatch; //Input latch this cycle
+ Bit8u status_latch;
+
+ //Status Register data;
+ Bit8u rw_mode; //2-bit R/W mode from command word register.
+ Bit8u mode; //3-bit mode from command word register.
+ bool bcd_mode; //1-bit BCD vs. Binary setting.
+ bool null_count; //Null count bit of status register.
+
+ //Latch status data;
+ bool count_LSB_latched;
+ bool count_MSB_latched;
+ bool status_latched;
+
+ //Miscelaneous State;
+ Bit32u count_binary; //Value of the count in binary.
+ bool triggerGATE; //Whether we saw GATE rise this cycle.
+ rw_status write_state; //Read state this cycle
+ rw_status read_state; //Read state this cycle
+ bool count_written; //Whether a count written since programmed
+ bool first_pass; //Whether or not this is the first loaded count.
+ bool state_bit_1; //Miscelaneous state bits.
+ bool state_bit_2;
+ Bit32u next_change_time; //Next time something besides count changes.
+ //0 means never.
+ };
+
+ counter_type counter[3];
+
+ Bit8u controlword;
+
+ int seen_problems;
+
+ void latch_counter(counter_type & thisctr);
+
+ void set_OUT (counter_type & thisctr, bool data);
+
+ void set_count (counter_type & thisctr, Bit32u data) BX_CPP_AttrRegparmN(2);
+
+ void set_count_to_binary (counter_type & thisctr) BX_CPP_AttrRegparmN(1);
+
+ void set_binary_to_count (counter_type & thisctr) BX_CPP_AttrRegparmN(1);
+
+ void decrement (counter_type & thisctr) BX_CPP_AttrRegparmN(1);
+
+ void decrement_multiple(counter_type & thisctr, Bit32u cycles) BX_CPP_AttrRegparmN(2);
+
+ void clock(Bit8u cnum) BX_CPP_AttrRegparmN(1);
+
+ void print_counter(counter_type & thisctr);
+
+#ifdef BX_USE_VMX
+ void write_initcount_vmx(Bit8u cnum);
+#endif
+
+public:
+ void init (void);
+ void reset (unsigned type);
+ pit_82C54 (void);
+
+ void clock_all(Bit32u cycles);
+ void clock_multiple(Bit8u cnum, Bit32u cycles);
+
+ Bit8u read(Bit8u address);
+ void write(Bit8u address, Bit8u data);
+
+ void set_GATE(Bit8u cnum, bool data);
+ bool read_GATE(Bit8u cnum);
+
+ bool read_OUT(Bit8u cnum);
+
+ Bit32u get_clock_event_time(Bit8u cnum);
+ Bit32u get_next_event_time(void);
+
+ void print_cnum(Bit8u cnum);
+
+};
+
+#endif
diff --git a/tools/ioemu/iodev/pit_wrap.cc b/tools/ioemu/iodev/pit_wrap.cc
new file mode 100644
index 0000000000..5fd1721073
--- /dev/null
+++ b/tools/ioemu/iodev/pit_wrap.cc
@@ -0,0 +1,444 @@
+////////////////////////////////////////////////////////////////////////
+// $Id: pit_wrap.cc,v 1.52 2003/08/19 00:10:38 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+#include "bochs.h"
+
+#if BX_USE_NEW_PIT
+
+#include "pit_wrap.h"
+
+
+//Important constant #defines:
+#define USEC_PER_SECOND (1000000)
+//1.193181MHz Clock
+#define TICKS_PER_SECOND (1193181)
+
+
+// define a macro to convert floating point numbers into 64-bit integers.
+// In MSVC++ you can convert a 64-bit float into a 64-bit signed integer,
+// but it will not convert a 64-bit float into a 64-bit unsigned integer.
+// This macro works around that.
+#define F2I(x) ((Bit64u)(Bit64s) (x))
+#define I2F(x) ((double)(Bit64s) (x))
+
+//DEBUG configuration:
+
+//Set up Logging.
+#define LOG_THIS bx_pit.
+
+//A single instance.
+bx_pit_c bx_pit;
+#if BX_USE_PIT_SMF
+#define this (&bx_pit)
+#endif
+
+//Workaround for environments where OUT is defined.
+#ifdef OUT
+# undef OUT
+#endif
+
+
+//Generic MAX and MIN Functions
+#define BX_MAX(a,b) ( ((a)>(b))?(a):(b) )
+#define BX_MIN(a,b) ( ((a)>(b))?(b):(a) )
+
+
+//USEC_ALPHA is multiplier for the past.
+//USEC_ALPHA_B is 1-USEC_ALPHA, or multiplier for the present.
+#define USEC_ALPHA ((double)(.8))
+#define USEC_ALPHA_B ((double)(((double)1)-USEC_ALPHA))
+#define USEC_ALPHA2 ((double)(.5))
+#define USEC_ALPHA2_B ((double)(((double)1)-USEC_ALPHA2))
+#define ALPHA_LOWER(old,new) ((Bit64u)((old<new)?((USEC_ALPHA*(I2F(old)))+(USEC_ALPHA_B*(I2F(new)))):((USEC_ALPHA2*(I2F(old)))+(USEC_ALPHA2_B*(I2F(new))))))
+
+
+//PIT tick to usec conversion functions:
+//Direct conversions:
+#define TICKS_TO_USEC(a) ( ((a)*USEC_PER_SECOND)/TICKS_PER_SECOND )
+#define USEC_TO_TICKS(a) ( ((a)*TICKS_PER_SECOND)/USEC_PER_SECOND )
+
+
+bx_pit_c::bx_pit_c( void )
+{
+ put("PIT");
+ settype(PITLOG);
+ s.speaker_data_on=0;
+
+ /* 8254 PIT (Programmable Interval Timer) */
+
+ BX_PIT_THIS s.timer_handle[1] = BX_NULL_TIMER_HANDLE;
+ BX_PIT_THIS s.timer_handle[2] = BX_NULL_TIMER_HANDLE;
+ BX_PIT_THIS s.timer_handle[0] = BX_NULL_TIMER_HANDLE;
+}
+
+bx_pit_c::~bx_pit_c( void )
+{
+}
+
+ int
+bx_pit_c::init( void )
+{
+ DEV_register_irq(0, "8254 PIT");
+ DEV_register_ioread_handler(this, read_handler, 0x0040, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0041, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0042, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0043, "8254 PIT", 1);
+ DEV_register_ioread_handler(this, read_handler, 0x0061, "8254 PIT", 1);
+
+ DEV_register_iowrite_handler(this, write_handler, 0x0040, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0041, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0042, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0043, "8254 PIT", 1);
+ DEV_register_iowrite_handler(this, write_handler, 0x0061, "8254 PIT", 1);
+
+ BX_DEBUG(("pit: starting init"));
+
+ BX_PIT_THIS s.speaker_data_on = 0;
+ BX_PIT_THIS s.refresh_clock_div2 = 0;
+
+ BX_PIT_THIS s.timer.init();
+
+ Bit64u my_time_usec = bx_virt_timer.time_usec();
+
+ if (BX_PIT_THIS s.timer_handle[0] == BX_NULL_TIMER_HANDLE) {
+ BX_PIT_THIS s.timer_handle[0] = bx_virt_timer.register_timer(this, timer_handler, (unsigned) 100 , 1, 1, "pit_wrap");
+ }
+ BX_DEBUG(("pit: RESETting timer."));
+ bx_virt_timer.deactivate_timer(BX_PIT_THIS s.timer_handle[0]);
+ BX_DEBUG(("deactivated timer."));
+ if(BX_PIT_THIS s.timer.get_next_event_time()) {
+ bx_virt_timer.activate_timer(BX_PIT_THIS s.timer_handle[0],
+ (Bit32u)BX_MAX(1,TICKS_TO_USEC(BX_PIT_THIS s.timer.get_next_event_time())),
+ 0);
+ BX_DEBUG(("activated timer."));
+ }
+ BX_PIT_THIS s.last_next_event_time = BX_PIT_THIS s.timer.get_next_event_time();
+ BX_PIT_THIS s.last_usec=my_time_usec;
+
+ BX_PIT_THIS s.total_ticks=0;
+ BX_PIT_THIS s.total_usec=0;
+
+ BX_DEBUG(("pit: finished init"));
+
+ BX_DEBUG(("s.last_usec="FMT_LL"d",BX_PIT_THIS s.last_usec));
+ BX_DEBUG(("s.timer_id=%d",BX_PIT_THIS s.timer_handle[0]));
+ BX_DEBUG(("s.timer.get_next_event_time=%d",BX_PIT_THIS s.timer.get_next_event_time()));
+ BX_DEBUG(("s.last_next_event_time=%d",BX_PIT_THIS s.last_next_event_time));
+
+ return(1);
+}
+
+ void
+bx_pit_c::reset(unsigned type)
+{
+}
+
+void
+bx_pit_c::timer_handler(void *this_ptr) {
+ bx_pit_c * class_ptr = (bx_pit_c *) this_ptr;
+
+ class_ptr->handle_timer();
+}
+
+void
+bx_pit_c::handle_timer() {
+ Bit64u my_time_usec = bx_virt_timer.time_usec();
+ Bit64u time_passed = my_time_usec-BX_PIT_THIS s.last_usec;
+ Bit32u time_passed32 = (Bit32u)time_passed;
+
+ BX_DEBUG(("pit: entering timer handler"));
+
+ if(time_passed32) {
+ periodic(time_passed32);
+ }
+ BX_PIT_THIS s.last_usec=BX_PIT_THIS s.last_usec + time_passed;
+ if(time_passed ||
+ (BX_PIT_THIS s.last_next_event_time
+ != BX_PIT_THIS s.timer.get_next_event_time())
+ ) {
+ BX_DEBUG(("pit: RESETting timer."));
+ bx_virt_timer.deactivate_timer(BX_PIT_THIS s.timer_handle[0]);
+ BX_DEBUG(("deactivated timer."));
+ if(BX_PIT_THIS s.timer.get_next_event_time()) {
+ bx_virt_timer.activate_timer(BX_PIT_THIS s.timer_handle[0],
+ (Bit32u)BX_MAX(1,TICKS_TO_USEC(BX_PIT_THIS s.timer.get_next_event_time())),
+ 0);
+ BX_DEBUG(("activated timer."));
+ }
+ BX_PIT_THIS s.last_next_event_time = BX_PIT_THIS s.timer.get_next_event_time();
+ }
+ BX_DEBUG(("s.last_usec="FMT_LL"d",BX_PIT_THIS s.last_usec));
+ BX_DEBUG(("s.timer_id=%d",BX_PIT_THIS s.timer_handle[0]));
+ BX_DEBUG(("s.timer.get_next_event_time=%x",BX_PIT_THIS s.timer.get_next_event_time()));
+ BX_DEBUG(("s.last_next_event_time=%d",BX_PIT_THIS s.last_next_event_time));
+}
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_pit_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_PIT_SMF
+ bx_pit_c *class_ptr = (bx_pit_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_pit_c::read( Bit32u address, unsigned int io_len )
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIT_SMF
+ BX_DEBUG(("pit: entering read handler"));
+
+ handle_timer();
+
+ Bit64u my_time_usec = bx_virt_timer.time_usec();
+
+ if (bx_dbg.pit)
+ BX_INFO(("pit: io read from port %04x", (unsigned) address));
+
+ switch (address) {
+
+ case 0x40: /* timer 0 - system ticks */
+ return(BX_PIT_THIS s.timer.read(0));
+ break;
+ case 0x41: /* timer 1 read */
+ return(BX_PIT_THIS s.timer.read(1));
+ break;
+ case 0x42: /* timer 2 read */
+ return(BX_PIT_THIS s.timer.read(2));
+ break;
+ case 0x43: /* timer 1 read */
+ return(BX_PIT_THIS s.timer.read(3));
+ break;
+
+ case 0x61:
+ /* AT, port 61h */
+ BX_PIT_THIS s.refresh_clock_div2 = (bx_bool)((my_time_usec / 15) & 1);
+ return( (BX_PIT_THIS s.timer.read_OUT(2)<<5) |
+ (BX_PIT_THIS s.refresh_clock_div2<<4) |
+ (BX_PIT_THIS s.speaker_data_on<<1) |
+ (BX_PIT_THIS s.timer.read_GATE(2)?1:0) );
+ break;
+
+ default:
+ BX_PANIC(("pit: unsupported io read from port %04x", address));
+ }
+ return(0); /* keep compiler happy */
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_pit_c::write_handler(void *this_ptr, Bit32u address, Bit32u dvalue, unsigned io_len)
+{
+#if !BX_USE_PIT_SMF
+ bx_pit_c *class_ptr = (bx_pit_c *) this_ptr;
+
+ class_ptr->write(address, dvalue, io_len);
+}
+
+ void
+bx_pit_c::write( Bit32u address, Bit32u dvalue,
+ unsigned int io_len )
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_PIT_SMF
+ Bit8u value;
+ Bit64u my_time_usec = bx_virt_timer.time_usec();
+ Bit64u time_passed = my_time_usec-BX_PIT_THIS s.last_usec;
+ Bit32u time_passed32 = (Bit32u)time_passed;
+
+ BX_DEBUG(("pit: entering write handler"));
+
+ if(time_passed32) {
+ periodic(time_passed32);
+ }
+ BX_PIT_THIS s.last_usec=BX_PIT_THIS s.last_usec + time_passed;
+
+ value = (Bit8u ) dvalue;
+
+ if (bx_dbg.pit)
+ BX_INFO(("pit: write to port %04x = %02x",
+ (unsigned) address, (unsigned) value));
+
+ switch (address) {
+ case 0x40: /* timer 0: write count register */
+ BX_PIT_THIS s.timer.write(0,value);
+ break;
+
+ case 0x41: /* timer 1: write count register */
+ BX_PIT_THIS s.timer.write( 1,value );
+ break;
+
+ case 0x42: /* timer 2: write count register */
+ BX_PIT_THIS s.timer.write( 2,value );
+ break;
+
+ case 0x43: /* timer 0-2 mode control */
+ BX_PIT_THIS s.timer.write( 3,value );
+ break;
+
+ case 0x61:
+ BX_PIT_THIS s.speaker_data_on = (value >> 1) & 0x01;
+/*??? only on AT+ */
+ BX_PIT_THIS s.timer.set_GATE(2, value & 0x01);
+#if BX_CPU_LEVEL < 2
+ /* ??? XT: */
+ bx_kbd_port61h_write(value);
+#endif
+ break;
+
+ default:
+ BX_PANIC(("pit: unsupported io write to port %04x = %02x",
+ (unsigned) address, (unsigned) value));
+ }
+
+#ifndef BX_VMX_PIT
+ if ((BX_PIT_THIS s.timer.read_OUT(0))==1) {
+ DEV_pic_raise_irq(0);
+ } else {
+ DEV_pic_lower_irq(0);
+ }
+#endif
+
+ if(time_passed ||
+ (BX_PIT_THIS s.last_next_event_time
+ != BX_PIT_THIS s.timer.get_next_event_time())
+ ) {
+ BX_DEBUG(("pit: RESETting timer."));
+ bx_virt_timer.deactivate_timer(BX_PIT_THIS s.timer_handle[0]);
+ BX_DEBUG(("deactivated timer."));
+ if(BX_PIT_THIS s.timer.get_next_event_time()) {
+ bx_virt_timer.activate_timer(BX_PIT_THIS s.timer_handle[0],
+ (Bit32u)BX_MAX(1,TICKS_TO_USEC(BX_PIT_THIS s.timer.get_next_event_time())),
+ 0);
+ BX_DEBUG(("activated timer."));
+ }
+ BX_PIT_THIS s.last_next_event_time = BX_PIT_THIS s.timer.get_next_event_time();
+ }
+ BX_DEBUG(("s.last_usec="FMT_LL"d",BX_PIT_THIS s.last_usec));
+ BX_DEBUG(("s.timer_id=%d",BX_PIT_THIS s.timer_handle[0]));
+ BX_DEBUG(("s.timer.get_next_event_time=%x",BX_PIT_THIS s.timer.get_next_event_time()));
+ BX_DEBUG(("s.last_next_event_time=%d",BX_PIT_THIS s.last_next_event_time));
+
+}
+
+
+
+
+ int
+bx_pit_c::SaveState( class state_file *fd )
+{
+ fd->write_check ("8254 start");
+ fd->write (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s));
+ fd->write_check ("8254 end");
+ return(0);
+}
+
+
+ int
+bx_pit_c::LoadState( class state_file *fd )
+{
+ fd->read_check ("8254 start");
+ fd->read (&BX_PIT_THIS s, sizeof (BX_PIT_THIS s));
+ fd->read_check ("8254 end");
+ return(0);
+}
+
+
+#if 0
+ void
+bx_kbd_port61h_write(Bit8u value)
+{
+// PcError("KBD_PORT61H_WRITE(): not implemented yet");
+ UNUSED( value );
+}
+#endif
+
+
+ bx_bool
+bx_pit_c::periodic( Bit32u usec_delta )
+{
+ bx_bool prev_timer0_out = BX_PIT_THIS s.timer.read_OUT(0);
+ bx_bool want_interrupt = 0;
+ Bit32u ticks_delta = 0;
+
+#ifdef BX_SCHEDULED_DIE_TIME
+ if (bx_pc_system.time_ticks() > BX_SCHEDULED_DIE_TIME) {
+ BX_ERROR (("ticks exceeded scheduled die time, quitting"));
+ BX_EXIT (2);
+ }
+#endif
+
+ BX_PIT_THIS s.total_usec += usec_delta;
+ ticks_delta=(Bit32u)((USEC_TO_TICKS((Bit64u)(BX_PIT_THIS s.total_usec)))-BX_PIT_THIS s.total_ticks);
+ BX_PIT_THIS s.total_ticks += ticks_delta;
+
+ while ((BX_PIT_THIS s.total_ticks >= TICKS_PER_SECOND) && (BX_PIT_THIS s.total_usec >= USEC_PER_SECOND)) {
+ BX_PIT_THIS s.total_ticks -= TICKS_PER_SECOND;
+ BX_PIT_THIS s.total_usec -= USEC_PER_SECOND;
+ }
+
+ while(ticks_delta>0) {
+ Bit32u maxchange=BX_PIT_THIS s.timer.get_next_event_time();
+ Bit32u timedelta=maxchange;
+ if((maxchange==0) || (maxchange>ticks_delta)) {
+ timedelta=ticks_delta;
+ }
+ BX_PIT_THIS s.timer.clock_all(timedelta);
+ if ( (prev_timer0_out==0) ) {
+ if ((BX_PIT_THIS s.timer.read_OUT(0))==1) {
+#ifndef BX_VMX_PIT
+ DEV_pic_raise_irq(0);
+#endif
+ prev_timer0_out=1;
+ }
+ } else {
+ if ((BX_PIT_THIS s.timer.read_OUT(0))==0) {
+#ifndef BX_VMX_PIT
+ DEV_pic_lower_irq(0);
+#endif
+ prev_timer0_out=0;
+ }
+ }
+ prev_timer0_out=BX_PIT_THIS s.timer.read_OUT(0);
+ ticks_delta-=timedelta;
+ }
+
+ return(want_interrupt);
+}
+
+#endif // #if BX_USE_NEW_PIT
diff --git a/tools/ioemu/iodev/pit_wrap.h b/tools/ioemu/iodev/pit_wrap.h
new file mode 100644
index 0000000000..e45e1e6b57
--- /dev/null
+++ b/tools/ioemu/iodev/pit_wrap.h
@@ -0,0 +1,104 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: pit_wrap.h,v 1.17 2003/08/19 00:10:38 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#ifndef _BX_PIT_WRAP_H
+#define _BX_PIT_WRAP_H
+
+#include "bochs.h"
+
+#if BX_USE_NEW_PIT
+
+#include "pit82c54.h"
+
+#if BX_USE_PIT_SMF
+# define BX_PIT_SMF static
+# define BX_PIT_THIS bx_pit.
+#else
+# define BX_PIT_SMF
+# define BX_PIT_THIS this->
+#endif
+
+#ifdef OUT
+# undef OUT
+#endif
+
+class bx_pit_c : public logfunctions {
+public:
+ bx_pit_c( void );
+ ~bx_pit_c( void );
+ BX_PIT_SMF int init( void );
+ BX_PIT_SMF void reset( unsigned type);
+ BX_PIT_SMF bx_bool periodic( Bit32u usec_delta );
+
+ BX_PIT_SMF int SaveState( class state_file *fd );
+ BX_PIT_SMF int LoadState( class state_file *fd );
+
+private:
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_PIT_SMF
+ Bit32u read( Bit32u addr, unsigned int len );
+ void write( Bit32u addr, Bit32u Value, unsigned int len );
+#endif
+
+ struct s_type {
+ pit_82C54 timer;
+ Bit8u speaker_data_on;
+ bx_bool refresh_clock_div2;
+ int timer_handle[3];
+ Bit64u last_usec;
+ Bit32u last_next_event_time;
+ Bit64u total_ticks;
+ Bit64u usec_per_second;
+ Bit64u ticks_per_second;
+ Bit64u total_sec;
+ Bit64u last_time;
+ Bit64u last_sec_usec;
+ Bit64u max_ticks;
+ Bit64u stored_delta;
+ Bit64u total_usec;
+ Bit64u em_last_realtime;
+ Bit64u last_realtime_delta;
+ Bit64u last_realtime_ticks;
+ } s;
+
+ static void timer_handler(void *this_ptr);
+ BX_PIT_SMF void handle_timer();
+
+ BX_PIT_SMF void write_count_reg( Bit8u value, unsigned timerid );
+ BX_PIT_SMF Bit8u read_counter( unsigned timerid );
+ BX_PIT_SMF void latch( unsigned timerid );
+ BX_PIT_SMF void set_GATE(unsigned pit_id, unsigned value);
+ BX_PIT_SMF void start(unsigned timerid);
+
+ BX_PIT_SMF void second_update_data(void);
+};
+
+extern bx_pit_c bx_pit;
+
+#endif // #if BX_USE_NEW_PIT
+#endif // #ifndef _BX_PIT_WRAP_H
diff --git a/tools/ioemu/iodev/plugin.cc b/tools/ioemu/iodev/plugin.cc
new file mode 100644
index 0000000000..136065d9d8
--- /dev/null
+++ b/tools/ioemu/iodev/plugin.cc
@@ -0,0 +1,554 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: plugin.cc,v 1.8 2003/07/31 12:04:47 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// This file defines the plugin and plugin-device registration functions and
+// the device registration functions. It handles dynamic loading of modules,
+// using the LTDL library for cross-platform support.
+//
+// This file is based on the plugin.c file from plex86, but with significant
+// changes to make it work in Bochs.
+// Plex86 is Copyright (C) 1999-2000 The plex86 developers team
+//
+/////////////////////////////////////////////////////////////////////////
+
+#include "bochs.h"
+#include "plugin.h"
+
+#define LOG_THIS genlog->
+
+#define PLUGIN_INIT_FMT_STRING "lib%s_LTX_plugin_init"
+#define PLUGIN_FINI_FMT_STRING "lib%s_LTX_plugin_fini"
+#define PLUGIN_PATH ""
+
+#ifndef WIN32
+#define PLUGIN_FILENAME_FORMAT "libbx_%s.la"
+#else
+#define PLUGIN_FILENAME_FORMAT "bx_%s.dll"
+#endif
+
+
+
+void (*pluginRegisterIRQ)(unsigned irq, const char* name) = 0;
+void (*pluginUnregisterIRQ)(unsigned irq, const char* name) = 0;
+
+void (* pluginResetSignal)(unsigned sig) = 0;
+
+void (*pluginSetHRQ)(unsigned val) = 0;
+void (*pluginSetHRQHackCallback)( void (*callback)(void) ) = 0;
+
+int (*pluginRegisterIOReadHandler)(void *thisPtr, ioReadHandler_t callback,
+ unsigned base, const char *name, Bit8u mask) = 0;
+int (*pluginRegisterIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback,
+ unsigned base, const char *name, Bit8u mask) = 0;
+int (*pluginRegisterDefaultIOReadHandler)(void *thisPtr, ioReadHandler_t callback,
+ const char *name, Bit8u mask) = 0;
+int (*pluginRegisterDefaultIOWriteHandler)(void *thisPtr, ioWriteHandler_t callback,
+ const char *name, Bit8u mask) = 0;
+int (*pluginRegisterTimer)(void *this_ptr, void (*funct)(void *),
+ Bit32u useconds, bx_bool continuous,
+ bx_bool active, const char* name) = 0;
+void (*pluginActivateTimer)(unsigned id, Bit32u usec, bx_bool continuous) = 0;
+
+void (*pluginHRQHackCallback)(void);
+unsigned pluginHRQ = 0;
+
+plugin_t *plugins = NULL; /* Head of the linked list of plugins */
+#if BX_PLUGINS
+static void plugin_init_one(plugin_t *plugin);
+#endif
+
+device_t *devices = NULL; /* Head of the linked list of registered devices */
+
+plugin_t *current_plugin_context = NULL;
+
+/************************************************************************/
+/* Builtins declarations */
+/************************************************************************/
+
+ static void
+builtinRegisterIRQ(unsigned irq, const char* name)
+{
+#if 0
+ pluginlog->panic("builtinRegisterIRQ called, no pic plugin loaded?");
+#else
+ bx_devices.register_irq(irq, name);
+#endif
+}
+
+ static void
+builtinUnregisterIRQ(unsigned irq, const char* name)
+{
+#if 0
+ pluginlog->panic("builtinUnregisterIRQ called, no pic plugin loaded?");
+#else
+ bx_devices.unregister_irq(irq, name);
+#endif
+}
+
+ static void
+builtinSetHRQ(unsigned val)
+{
+#if 0
+ pluginlog->panic("builtinSetHRQ called, no plugin loaded?");
+#else
+ pluginHRQ = val;
+#endif
+}
+
+ static void
+builtinSetHRQHackCallback( void (*callback)(void) )
+{
+#if 0
+ pluginlog->panic("builtinSetHRQHackCallback called, no plugin loaded?");
+#else
+ pluginHRQHackCallback = callback;
+#endif
+}
+
+ static void
+builtinResetSignal( unsigned )
+{
+ pluginlog->panic("builtinResetSignal called, no plugin loaded?");
+}
+
+ static int
+builtinRegisterIOReadHandler(void *thisPtr, ioReadHandler_t callback,
+ unsigned base, const char *name, Bit8u mask)
+{
+ BX_ASSERT (mask<8);
+ bx_devices.register_io_read_handler (thisPtr, callback, base, name, mask);
+ pluginlog->ldebug("plugin %s registered I/O read address at %04x", name, base);
+ return 0;
+}
+
+ static int
+builtinRegisterIOWriteHandler(void *thisPtr, ioWriteHandler_t callback,
+ unsigned base, const char *name, Bit8u mask)
+{
+ BX_ASSERT (mask<8);
+ bx_devices.register_io_write_handler (thisPtr, callback, base, name, mask);
+ pluginlog->ldebug("plugin %s registered I/O write address at %04x", name, base);
+ return 0;
+}
+
+ static int
+builtinRegisterDefaultIOReadHandler(void *thisPtr, ioReadHandler_t callback,
+ const char *name, Bit8u mask)
+{
+ BX_ASSERT (mask<8);
+ bx_devices.register_default_io_read_handler (thisPtr, callback, name, mask);
+ pluginlog->ldebug("plugin %s registered default I/O read ", name);
+ return 0;
+}
+
+ static int
+builtinRegisterDefaultIOWriteHandler(void *thisPtr, ioWriteHandler_t callback,
+ const char *name, Bit8u mask)
+{
+ BX_ASSERT (mask<8);
+ bx_devices.register_default_io_write_handler (thisPtr, callback, name, mask);
+ pluginlog->ldebug("plugin %s registered default I/O write ", name);
+ return 0;
+}
+
+ static int
+builtinRegisterTimer(void *this_ptr, void (*funct)(void *),
+ Bit32u useconds, bx_bool continuous,
+ bx_bool active, const char* name)
+{
+ int id = bx_pc_system.register_timer (this_ptr, funct, useconds, continuous, active, name);
+ pluginlog->ldebug("plugin %s registered timer %d", name, id);
+ return id;
+}
+
+ static void
+builtinActivateTimer(unsigned id, Bit32u usec, bx_bool continuous)
+{
+ bx_pc_system.activate_timer (id, usec, continuous);
+ pluginlog->ldebug("plugin activated timer %d", id);
+}
+
+#if BX_PLUGINS
+/************************************************************************/
+/* Plugin initialization / deinitialization */
+/************************************************************************/
+
+ void
+plugin_init_all (void)
+{
+ plugin_t *plugin;
+
+ pluginlog->info("Initializing plugins");
+
+ for (plugin = plugins; plugin; plugin = plugin->next)
+ {
+ char *arg_ptr = plugin->args;
+
+ /* process the command line */
+ plugin->argc = 0;
+ while (plugin->argc < MAX_ARGC)
+ {
+ while (*arg_ptr && isspace (*arg_ptr))
+ arg_ptr++;
+
+ if (!*arg_ptr)
+ break;
+ plugin->argv[plugin->argc++] = arg_ptr;
+
+ while (*arg_ptr && !isspace (*arg_ptr))
+ arg_ptr++;
+
+ if (!*arg_ptr)
+ break;
+ *arg_ptr++ = '\0';
+ }
+
+ /* initialize the plugin */
+ if (plugin->plugin_init (plugin, plugin->type, plugin->argc, plugin->argv))
+ {
+ pluginlog->panic("Plugin initialization failed for %s", plugin->name);
+ plugin_abort();
+ }
+
+ plugin->initialized = 1;
+ }
+
+ return;
+}
+
+void
+plugin_init_one(plugin_t *plugin)
+{
+ char *arg_ptr = plugin->args;
+
+ /* process the command line */
+ plugin->argc = 0;
+ while (plugin->argc < MAX_ARGC)
+ {
+ while (*arg_ptr && isspace (*arg_ptr))
+ arg_ptr++;
+
+ if (!*arg_ptr)
+ break;
+ plugin->argv[plugin->argc++] = arg_ptr;
+
+ while (*arg_ptr && !isspace (*arg_ptr))
+ arg_ptr++;
+
+ if (!*arg_ptr)
+ break;
+ *arg_ptr++ = '\0';
+ }
+
+ /* initialize the plugin */
+ if (plugin->plugin_init (plugin, plugin->type, plugin->argc, plugin->argv))
+ {
+ pluginlog->info("Plugin initialization failed for %s", plugin->name);
+ plugin_abort();
+ }
+
+ plugin->initialized = 1;
+}
+
+
+ plugin_t *
+plugin_unload(plugin_t *plugin)
+{
+ plugin_t *dead_plug;
+
+ if (plugin->initialized)
+ plugin->plugin_fini ();
+
+ lt_dlclose (plugin->handle);
+ free (plugin->name);
+ free (plugin->args);
+
+ dead_plug = plugin;
+ plugin = plugin->next;
+ free (dead_plug);
+
+ return plugin;
+}
+
+
+void
+plugin_fini_all (void)
+{
+ plugin_t *plugin;
+
+ for (plugin = plugins; plugin; plugin = plugin_unload (plugin));
+
+ return;
+}
+
+ void
+plugin_load (char *name, char *args, plugintype_t type)
+{
+ plugin_t *plugin;
+
+ plugin = (plugin_t *)malloc (sizeof (plugin_t));
+ if (!plugin)
+ {
+ BX_PANIC (("malloc plugin_t failed"));
+ }
+
+ plugin->type = type;
+ plugin->name = name;
+ plugin->args = args;
+ plugin->initialized = 0;
+
+ char plugin_filename[BX_PATHNAME_LEN], buf[BX_PATHNAME_LEN];
+ sprintf (buf, PLUGIN_FILENAME_FORMAT, name);
+ sprintf(plugin_filename, "%s%s", PLUGIN_PATH, buf);
+
+ // Set context so that any devices that the plugin registers will
+ // be able to see which plugin created them. The registration will
+ // be called from either dlopen (global constructors) or plugin_init.
+ BX_ASSERT (current_plugin_context == NULL);
+ current_plugin_context = plugin;
+ plugin->handle = lt_dlopen (plugin_filename);
+ BX_INFO (("lt_dlhandle is %p", plugin->handle));
+ if (!plugin->handle)
+ {
+ current_plugin_context = NULL;
+ BX_PANIC (("dlopen failed for module '%s': %s", name, lt_dlerror ()));
+ free (plugin);
+ return;
+ }
+
+ sprintf (buf, PLUGIN_INIT_FMT_STRING, name);
+ plugin->plugin_init =
+ (int (*)(struct _plugin_t *, enum plugintype_t, int, char *[])) /* monster typecast */
+ lt_dlsym (plugin->handle, buf);
+ if (plugin->plugin_init == NULL) {
+ pluginlog->panic("could not find plugin_init: %s", lt_dlerror ());
+ plugin_abort ();
+ }
+
+ sprintf (buf, PLUGIN_FINI_FMT_STRING, name);
+ plugin->plugin_fini = (void (*)(void)) lt_dlsym (plugin->handle, buf);
+ if (plugin->plugin_init == NULL) {
+ pluginlog->panic("could not find plugin_fini: %s", lt_dlerror ());
+ plugin_abort ();
+ }
+ pluginlog->info("loaded plugin %s",plugin_filename);
+
+
+ /* Insert plugin at the _end_ of the plugin linked list. */
+ plugin->next = NULL;
+
+ if (!plugins)
+ {
+ /* Empty list, this become the first entry. */
+ plugins = plugin;
+ }
+ else
+ {
+ /* Non-empty list. Add to end. */
+ plugin_t *temp = plugins;
+
+ while (temp->next)
+ temp = temp->next;
+
+ temp->next = plugin;
+ }
+
+ plugin_init_one(plugin);
+
+ // check that context didn't change. This should only happen if we
+ // need a reentrant plugin_load.
+ BX_ASSERT (current_plugin_context == plugin);
+ current_plugin_context = NULL;
+
+ return;
+}
+
+void
+plugin_abort (void)
+{
+ pluginlog->panic("plugin load aborted");
+}
+
+#endif /* end of #if BX_PLUGINS */
+
+/************************************************************************/
+/* Plugin system: initialisation of plugins entry points */
+/************************************************************************/
+
+ void
+plugin_startup(void)
+{
+ pluginRegisterIRQ = builtinRegisterIRQ;
+ pluginUnregisterIRQ = builtinUnregisterIRQ;
+
+ pluginResetSignal = builtinResetSignal;
+
+ pluginSetHRQHackCallback = builtinSetHRQHackCallback;
+ pluginSetHRQ = builtinSetHRQ;
+
+ pluginRegisterIOReadHandler = builtinRegisterIOReadHandler;
+ pluginRegisterIOWriteHandler = builtinRegisterIOWriteHandler;
+
+ pluginRegisterDefaultIOReadHandler = builtinRegisterDefaultIOReadHandler;
+ pluginRegisterDefaultIOWriteHandler = builtinRegisterDefaultIOWriteHandler;
+
+ pluginRegisterTimer = builtinRegisterTimer;
+ pluginActivateTimer = builtinActivateTimer;
+
+#if BX_PLUGINS
+ pluginlog = new logfunctions();
+ pluginlog->put("PLGIN");
+ pluginlog->settype(PLUGINLOG);
+ int status = lt_dlinit ();
+ if (status != 0) {
+ BX_ERROR (("initialization error in ltdl library (for loading plugins)"));
+ BX_PANIC (("error message was: %s", lt_dlerror ()));
+ }
+#endif
+}
+
+
+/************************************************************************/
+/* Plugin system: Device registration */
+/************************************************************************/
+
+void pluginRegisterDeviceDevmodel(plugin_t *plugin, plugintype_t type, bx_devmodel_c *devmodel, char *name)
+{
+ device_t *device;
+
+ device = (device_t *)malloc (sizeof (device_t));
+ if (!device)
+ {
+ pluginlog->panic("can't allocate device_t");
+ }
+
+ device->name = name;
+ BX_ASSERT (devmodel != NULL);
+ device->devmodel = devmodel;
+ device->plugin = plugin; // this can be NULL
+ device->use_devmodel_interface = 1;
+ device->device_init_mem = NULL; // maybe should use 1 to detect any use?
+ device->device_init_dev = NULL;
+ device->device_reset = NULL;
+ device->device_load_state = NULL;
+ device->device_save_state = NULL;
+ device->next = NULL;
+
+ // Don't add every kind of device to the list.
+ switch (type) {
+ case PLUGTYPE_CORE:
+ // Core devices are present whether or not we are using plugins, so
+ // they are managed by the same code in iodev/devices.cc whether
+ // plugins are on or off.
+ return; // Do not add core devices to the devices list.
+ case PLUGTYPE_OPTIONAL:
+ case PLUGTYPE_USER:
+ default:
+ // The plugin system will manage optional and user devices only.
+ break;
+ }
+
+ if (!devices)
+ {
+ /* Empty list, this become the first entry. */
+ devices = device;
+ }
+ else
+ {
+ /* Non-empty list. Add to end. */
+ device_t *temp = devices;
+
+ while (temp->next)
+ temp = temp->next;
+
+ temp->next = device;
+ }
+}
+
+/************************************************************************/
+/* Plugin system: Check if a plugin is loaded */
+/************************************************************************/
+
+bx_bool pluginDevicePresent(char *name)
+{
+ device_t *device;
+
+ for (device = devices; device; device = device->next)
+ {
+ if (strcmp(device->name,name)==0) return true;
+ }
+
+ return false;
+}
+
+#if BX_PLUGINS
+/************************************************************************/
+/* Plugin system: Load one plugin */
+/************************************************************************/
+
+int bx_load_plugin (const char *name, plugintype_t type)
+{
+ char *namecopy = new char[1+strlen(name)];
+ strcpy (namecopy, name);
+ plugin_load (namecopy, "", type);
+ return 0;
+}
+#endif /* end of #if BX_PLUGINS */
+
+/*************************************************************************/
+/* Plugin system: Execute init function of all registered plugin-devices */
+/*************************************************************************/
+
+void bx_init_plugins()
+{
+ device_t *device;
+
+ // two loops
+ for (device = devices; device; device = device->next)
+ {
+ if (!device->use_devmodel_interface) {
+ if (device->device_init_mem != NULL) {
+ pluginlog->info("init_mem of '%s' plugin device by function pointer",device->name);
+ device->device_init_mem(BX_MEM(0));
+ }
+ } else {
+ pluginlog->info("init_mem of '%s' plugin device by virtual method",device->name);
+ device->devmodel->init_mem (BX_MEM(0));
+ }
+ }
+
+ for (device = devices; device; device = device->next)
+ {
+ if (!device->use_devmodel_interface) {
+ if (device->device_init_dev != NULL) {
+ pluginlog->info("init_dev of '%s' plugin device by function pointer",device->name);
+ device->device_init_dev();
+ }
+ } else {
+ pluginlog->info("init_dev of '%s' plugin device by virtual method",device->name);
+ device->devmodel->init ();
+ }
+ }
+}
+
+/**************************************************************************/
+/* Plugin system: Execute reset function of all registered plugin-devices */
+/**************************************************************************/
+
+void bx_reset_plugins(unsigned signal)
+{
+ device_t *device;
+ for (device = devices; device; device = device->next)
+ {
+ if (!device->use_devmodel_interface) {
+ if (device->device_reset != NULL) {
+ pluginlog->info("reset of '%s' plugin device by function pointer",device->name);
+ device->device_reset(signal);
+ }
+ } else {
+ pluginlog->info("reset of '%s' plugin device by virtual method",device->name);
+ device->devmodel->reset (signal);
+ }
+ }
+}
diff --git a/tools/ioemu/iodev/scancodes.cc b/tools/ioemu/iodev/scancodes.cc
new file mode 100644
index 0000000000..63efed45f6
--- /dev/null
+++ b/tools/ioemu/iodev/scancodes.cc
@@ -0,0 +1,770 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: scancodes.cc,v 1.5 2002/10/24 21:07:51 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#include "scancodes.h"
+
+unsigned char translation8042[256] = {
+ 0xff,0x43,0x41,0x3f,0x3d,0x3b,0x3c,0x58,0x64,0x44,0x42,0x40,0x3e,0x0f,0x29,0x59,
+ 0x65,0x38,0x2a,0x70,0x1d,0x10,0x02,0x5a,0x66,0x71,0x2c,0x1f,0x1e,0x11,0x03,0x5b,
+ 0x67,0x2e,0x2d,0x20,0x12,0x05,0x04,0x5c,0x68,0x39,0x2f,0x21,0x14,0x13,0x06,0x5d,
+ 0x69,0x31,0x30,0x23,0x22,0x15,0x07,0x5e,0x6a,0x72,0x32,0x24,0x16,0x08,0x09,0x5f,
+ 0x6b,0x33,0x25,0x17,0x18,0x0b,0x0a,0x60,0x6c,0x34,0x35,0x26,0x27,0x19,0x0c,0x61,
+ 0x6d,0x73,0x28,0x74,0x1a,0x0d,0x62,0x6e,0x3a,0x36,0x1c,0x1b,0x75,0x2b,0x63,0x76,
+ 0x55,0x56,0x77,0x78,0x79,0x7a,0x0e,0x7b,0x7c,0x4f,0x7d,0x4b,0x47,0x7e,0x7f,0x6f,
+ 0x52,0x53,0x50,0x4c,0x4d,0x48,0x01,0x45,0x57,0x4e,0x51,0x4a,0x37,0x49,0x46,0x54,
+ 0x80,0x81,0x82,0x41,0x54,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
+ 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
+ 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
+ 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
+ 0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
+ 0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
+ 0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
+ 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
+ };
+
+
+// Definition of scancodes make and break,
+// for each set (mf1/xt , mf2/at , mf3/ps2)
+// The table must be in BX_KEY order
+//
+scancode scancodes[BX_KEY_NBKEYS][3] =
+{
+ { // BX_KEY_CTRL_L ( ibm 58)
+ { "\x1D" , "\x9D" },
+ { "\x14" , "\xF0\x14" },
+ { "\x11" , "\xF0\x11" },
+ },
+
+ { // BX_KEY_SHIFT_L ( ibm 44)
+ { "\x2A" , "\xAA" },
+ { "\x12" , "\xF0\x12" },
+ { "\x12" , "\xF0\x12" },
+ },
+
+ { // BX_KEY_F1 ( ibm 112 )
+ { "\x3B" , "\xBB" },
+ { "\x05" , "\xF0\x05" },
+ { "\x07" , "\xF0\x07" },
+ },
+
+ { // BX_KEY_F2 ( ibm 113 )
+ { "\x3C" , "\xBC" },
+ { "\x06" , "\xF0\x06" },
+ { "\x0F" , "\xF0\x0F" },
+ },
+
+ { // BX_KEY_F3 ( ibm 114 )
+ { "\x3D" , "\xBD" },
+ { "\x04" , "\xF0\x04" },
+ { "\x17" , "\xF0\x17" },
+ },
+
+ { // BX_KEY_F4 ( ibm 115 )
+ { "\x3E" , "\xBE" },
+ { "\x0C" , "\xF0\x0C" },
+ { "\x1F" , "\xF0\x1F" },
+ },
+
+ { // BX_KEY_F5 ( ibm 116 )
+ { "\x3F" , "\xBF" },
+ { "\x03" , "\xF0\x03" },
+ { "\x27" , "\xF0\x27" },
+ },
+
+ { // BX_KEY_F6 ( ibm 117 )
+ { "\x40" , "\xC0" },
+ { "\x0B" , "\xF0\x0B" },
+ { "\x2F" , "\xF0\x2F" },
+ },
+
+ { // BX_KEY_F7 ( ibm 118 )
+ { "\x41" , "\xC1" },
+ { "\x83" , "\xF0\x83" },
+ { "\x37" , "\xF0\x37" },
+},
+
+ { // BX_KEY_F8 ( ibm 119 )
+ { "\x42" , "\xC2" },
+ { "\x0A" , "\xF0\x0A" },
+ { "\x3F" , "\xF0\x3F" },
+ },
+
+ { // BX_KEY_F9 ( ibm 120 )
+ { "\x43" , "\xC3" },
+ { "\x01" , "\xF0\x01" },
+ { "\x47" , "\xF0\x47" },
+ },
+
+ { // BX_KEY_F10 ( ibm 121 )
+ { "\x44" , "\xC4" },
+ { "\x09" , "\xF0\x09" },
+ { "\x4F" , "\xF0\x4F" },
+ },
+
+ { // BX_KEY_F11 ( ibm 122 )
+ { "\x57" , "\xD7" },
+ { "\x78" , "\xF0\x78" },
+ { "\x56" , "\xF0\x56" },
+ },
+
+ { // BX_KEY_F12 ( ibm 123 )
+ { "\x58" , "\xD8" },
+ { "\x07" , "\xF0\x07" },
+ { "\x5E" , "\xF0\x5E" },
+ },
+
+ { // BX_KEY_CTRL_R ( ibm 64 )
+ { "\xE0\x1D" , "\xE0\x9D" },
+ { "\xE0\x14" , "\xE0\xF0\x14" },
+ { "\x58" , "\xF0x58" },
+ },
+
+ { // BX_KEY_SHIFT_R ( ibm 57 )
+ { "\x36" , "\xB6" },
+ { "\x59" , "\xF0\x59" },
+ { "\x59" , "\xF0\x59" },
+ },
+
+ { // BX_KEY_CAPS_LOCK ( ibm 30 )
+ { "\x3A" , "\xBA" },
+ { "\x58" , "\xF0\x58" },
+ { "\x14" , "\xF0\x14" },
+ },
+
+ { // BX_KEY_NUM_LOCK ( ibm 90 )
+ { "\x45" , "\xC5" },
+ { "\x77" , "\xF0\x77" },
+ { "\x76" , "\xF0\x76" },
+ },
+
+ { // BX_KEY_ALT_L ( ibm 60 )
+ { "\x38" , "\xB8" },
+ { "\x11" , "\xF0\x11" },
+ { "\x19" , "\xF0\x19" },
+ },
+
+ { // BX_KEY_ALT_R ( ibm 62 )
+ { "\xE0\x38" , "\xE0\xB8" },
+ { "\xE0\x11" , "\xE0\xF0\x11" },
+ { "\x39" , "\xF0\x39" },
+ },
+
+ { // BX_KEY_A ( ibm 31 )
+ { "\x1E" , "\x9E" },
+ { "\x1C" , "\xF0\x1C" },
+ { "\x1C" , "\xF0\x1C" },
+ },
+
+ { // BX_KEY_B ( ibm 50 )
+ { "\x30" , "\xB0" },
+ { "\x32" , "\xF0\x32" },
+ { "\x32" , "\xF0\x32" },
+ },
+
+ { // BX_KEY_C ( ibm 48 )
+ { "\x2E" , "\xAE" },
+ { "\x21" , "\xF0\x21" },
+ { "\x21" , "\xF0\x21" },
+ },
+
+ { // BX_KEY_D ( ibm 33 )
+ { "\x20" , "\xA0" },
+ { "\x23" , "\xF0\x23" },
+ { "\x23" , "\xF0\x23" },
+ },
+
+ { // BX_KEY_E ( ibm 19 )
+ { "\x12" , "\x92" },
+ { "\x24" , "\xF0\x24" },
+ { "\x24" , "\xF0\x24" },
+ },
+
+ { // BX_KEY_F ( ibm 34 )
+ { "\x21" , "\xA1" },
+ { "\x2B" , "\xF0\x2B" },
+ { "\x2B" , "\xF0\x2B" },
+ },
+
+ { // BX_KEY_G ( ibm 35 )
+ { "\x22" , "\xA2" },
+ { "\x34" , "\xF0\x34" },
+ { "\x34" , "\xF0\x34" },
+ },
+
+ { // BX_KEY_H ( ibm 36 )
+ { "\x23" , "\xA3" },
+ { "\x33" , "\xF0\x33" },
+ { "\x33" , "\xF0\x33" },
+ },
+
+ { // BX_KEY_I ( ibm 24 )
+ { "\x17" , "\x97" },
+ { "\x43" , "\xF0\x43" },
+ { "\x43" , "\xF0\x43" },
+ },
+
+ { // BX_KEY_J ( ibm 37 )
+ { "\x24" , "\xA4" },
+ { "\x3B" , "\xF0\x3B" },
+ { "\x3B" , "\xF0\x3B" },
+ },
+
+ { // BX_KEY_K ( ibm 38 )
+ { "\x25" , "\xA5" },
+ { "\x42" , "\xF0\x42" },
+ { "\x42" , "\xF0\x42" },
+ },
+
+ { // BX_KEY_L ( ibm 39 )
+ { "\x26" , "\xA6" },
+ { "\x4B" , "\xF0\x4B" },
+ { "\x4B" , "\xF0\x4B" },
+ },
+
+ { // BX_KEY_M ( ibm 52 )
+ { "\x32" , "\xB2" },
+ { "\x3A" , "\xF0\x3A" },
+ { "\x3A" , "\xF0\x3A" },
+ },
+
+ { // BX_KEY_N ( ibm 51 )
+ { "\x31" , "\xB1" },
+ { "\x31" , "\xF0\x31" },
+ { "\x31" , "\xF0\x31" },
+ },
+
+ { // BX_KEY_O ( ibm 25 )
+ { "\x18" , "\x98" },
+ { "\x44" , "\xF0\x44" },
+ { "\x44" , "\xF0\x44" },
+ },
+
+ { // BX_KEY_P ( ibm 26 )
+ { "\x19" , "\x99" },
+ { "\x4D" , "\xF0\x4D" },
+ { "\x4D" , "\xF0\x4D" },
+ },
+
+ { // BX_KEY_Q ( ibm 17 )
+ { "\x10" , "\x90" },
+ { "\x15" , "\xF0\x15" },
+ { "\x15" , "\xF0\x15" },
+ },
+
+ { // BX_KEY_R ( ibm 20 )
+ { "\x13" , "\x93" },
+ { "\x2D" , "\xF0\x2D" },
+ { "\x2D" , "\xF0\x2D" },
+ },
+
+ { // BX_KEY_S ( ibm 32 )
+ { "\x1F" , "\x9F" },
+ { "\x1B" , "\xF0\x1B" },
+ { "\x1B" , "\xF0\x1B" },
+ },
+
+ { // BX_KEY_T ( ibm 21 )
+ { "\x14" , "\x94" },
+ { "\x2C" , "\xF0\x2C" },
+ { "\x2C" , "\xF0\x2C" },
+ },
+
+ { // BX_KEY_U ( ibm 23 )
+ { "\x16" , "\x96" },
+ { "\x3C" , "\xF0\x3C" },
+ { "\x3C" , "\xF0\x3C" },
+ },
+
+ { // BX_KEY_V ( ibm 49 )
+ { "\x2F" , "\xAF" },
+ { "\x2A" , "\xF0\x2A" },
+ { "\x2A" , "\xF0\x2A" },
+ },
+
+ { // BX_KEY_W ( ibm 18 )
+ { "\x11" , "\x91" },
+ { "\x1D" , "\xF0\x1D" },
+ { "\x1D" , "\xF0\x1D" },
+ },
+
+ { // BX_KEY_X ( ibm 47 )
+ { "\x2D" , "\xAD" },
+ { "\x22" , "\xF0\x22" },
+ { "\x22" , "\xF0\x22" },
+ },
+
+ { // BX_KEY_Y ( ibm 22 )
+ { "\x15" , "\x95" },
+ { "\x35" , "\xF0\x35" },
+ { "\x35" , "\xF0\x35" },
+ },
+
+ { // BX_KEY_Z ( ibm 46 )
+ { "\x2C" , "\xAC" },
+ { "\x1A" , "\xF0\x1A" },
+ { "\x1A" , "\xF0\x1A" },
+ },
+
+ { // BX_KEY_0 ( ibm 11 )
+ { "\x0B" , "\x8B" },
+ { "\x45" , "\xF0\x45" },
+ { "\x45" , "\xF0\x45" },
+ },
+
+ { // BX_KEY_1 ( ibm 2 )
+ { "\x02" , "\x82" },
+ { "\x16" , "\xF0\x16" },
+ { "\x16" , "\xF0\x16" },
+ },
+
+ { // BX_KEY_2 ( ibm 3 )
+ { "\x03" , "\x83" },
+ { "\x1E" , "\xF0\x1E" },
+ { "\x1E" , "\xF0\x1E" },
+ },
+
+ { // BX_KEY_3 ( ibm 4 )
+ { "\x04" , "\x84" },
+ { "\x26" , "\xF0\x26" },
+ { "\x26" , "\xF0\x26" },
+ },
+
+ { // BX_KEY_4 ( ibm 5 )
+ { "\x05" , "\x85" },
+ { "\x25" , "\xF0\x25" },
+ { "\x25" , "\xF0\x25" },
+ },
+
+ { // BX_KEY_5 ( ibm 6 )
+ { "\x06" , "\x86" },
+ { "\x2E" , "\xF0\x2E" },
+ { "\x2E" , "\xF0\x2E" },
+ },
+
+ { // BX_KEY_6 ( ibm 7 )
+ { "\x07" , "\x87" },
+ { "\x36" , "\xF0\x36" },
+ { "\x36" , "\xF0\x36" },
+ },
+
+ { // BX_KEY_7 ( ibm 8 )
+ { "\x08" , "\x88" },
+ { "\x3D" , "\xF0\x3D" },
+ { "\x3D" , "\xF0\x3D" },
+ },
+
+ { // BX_KEY_8 ( ibm 9 )
+ { "\x09" , "\x89" },
+ { "\x3E" , "\xF0\x3E" },
+ { "\x3E" , "\xF0\x3E" },
+ },
+
+ { // BX_KEY_9 ( ibm 10 )
+ { "\x0A" , "\x8A" },
+ { "\x46" , "\xF0\x46" },
+ { "\x46" , "\xF0\x46" },
+ },
+
+ { // BX_KEY_ESC ( ibm 110 )
+ { "\x01" , "\x81" },
+ { "\x76" , "\xF0\x76" },
+ { "\x08" , "\xF0\x08" },
+ },
+
+ { // BX_KEY_SPACE ( ibm 61 )
+ { "\x39" , "\xB9" },
+ { "\x29" , "\xF0\x29" },
+ { "\x29" , "\xF0\x29" },
+ },
+
+ { // BX_KEY_SINGLE_QUOTE ( ibm 41 )
+ { "\x28" , "\xA8" },
+ { "\x52" , "\xF0\x52" },
+ { "\x52" , "\xF0\x52" },
+ },
+
+ { // BX_KEY_COMMA ( ibm 53 )
+ { "\x33" , "\xB3" },
+ { "\x41" , "\xF0\x41" },
+ { "\x41" , "\xF0\x41" },
+ },
+
+ { // BX_KEY_PERIOD ( ibm 54 )
+ { "\x34" , "\xB4" },
+ { "\x49" , "\xF0\x49" },
+ { "\x49" , "\xF0\x49" },
+ },
+
+ { // BX_KEY_SLASH ( ibm 55 )
+ { "\x35" , "\xB5" },
+ { "\x4A" , "\xF0\x4A" },
+ { "\x4A" , "\xF0\x4A" },
+ },
+
+ { // BX_KEY_SEMICOLON ( ibm 40 )
+ { "\x27" , "\xA7" },
+ { "\x4C" , "\xF0\x4C" },
+ { "\x4C" , "\xF0\x4C" },
+ },
+
+ { // BX_KEY_EQUALS ( ibm 13 )
+ { "\x0D" , "\x8D" },
+ { "\x55" , "\xF0\x55" },
+ { "\x55" , "\xF0\x55" },
+ },
+
+ { // BX_KEY_LEFT_BRACKET ( ibm 27 )
+ { "\x1A" , "\x9A" },
+ { "\x54" , "\xF0\x54" },
+ { "\x54" , "\xF0\x54" },
+ },
+
+ { // BX_KEY_BACKSLASH ( ibm 42, 29)
+ { "\x2B" , "\xAB" },
+ { "\x5D" , "\xF0\x5D" },
+ { "\x53" , "\xF0\x53" },
+ },
+
+ { // BX_KEY_RIGHT_BRACKET ( ibm 28 )
+ { "\x1B" , "\x9B" },
+ { "\x5B" , "\xF0\x5B" },
+ { "\x5B" , "\xF0\x5B" },
+ },
+
+ { // BX_KEY_MINUS ( ibm 12 )
+ { "\x0C" , "\x8C" },
+ { "\x4E" , "\xF0\x4E" },
+ { "\x4E" , "\xF0\x4E" },
+ },
+
+ { // BX_KEY_GRAVE ( ibm 1 )
+ { "\x29" , "\xA9" },
+ { "\x0E" , "\xF0\x0E" },
+ { "\x0E" , "\xF0\x0E" },
+ },
+
+ { // BX_KEY_BACKSPACE ( ibm 15 )
+ { "\x0E" , "\x8E" },
+ { "\x66" , "\xF0\x66" },
+ { "\x66" , "\xF0\x66" },
+ },
+
+ { // BX_KEY_ENTER ( ibm 43 )
+ { "\x1C" , "\x9C" },
+ { "\x5A" , "\xF0\x5A" },
+ { "\x5A" , "\xF0\x5A" },
+ },
+
+ { // BX_KEY_TAB ( ibm 16 )
+ { "\x0F" , "\x8F" },
+ { "\x0D" , "\xF0\x0D" },
+ { "\x0D" , "\xF0\x0D" },
+ },
+
+ { // BX_KEY_LEFT_BACKSLASH ( ibm 45 )
+ { "\x56" , "\xD6" },
+ { "\x61" , "\xF0\x61" },
+ { "\x13" , "\xF0\x13" },
+ },
+
+ { // BX_KEY_PRINT ( ibm 124 )
+ { "\xE0\x37" , "\xE0\xB7" },
+ { "\xE0\x7C" , "\xE0\xF0\x7C" },
+ { "\x57" , "\xF0\x57" },
+ },
+
+ { // BX_KEY_SCRL_LOCK ( ibm 125 )
+ { "\x46" , "\xC6" },
+ { "\x7E" , "\xF0\x7E" },
+ { "\x5F" , "\xF0\x5F" },
+ },
+
+ { // BX_KEY_PAUSE ( ibm 126 )
+ { "\xE1\x1D\x45\xE1\x9D\xC5" , "" },
+ { "\xE1\x14\x77\xE1\xF0\x14\xF0\x77" , "" },
+ { "\x62" , "\xF0\x62" },
+ },
+
+ { // BX_KEY_INSERT ( ibm 75 )
+ { "\xE0\x52" , "\xE0\xD2" },
+ { "\xE0\x70" , "\xE0\xF0\x70" },
+ { "\x67" , "\xF0\x67" },
+ },
+
+ { // BX_KEY_DELETE ( ibm 76 )
+ { "\xE0\x53" , "\xE0\xD3" },
+ { "\xE0\x71" , "\xE0\xF0\x71" },
+ { "\x64" , "\xF0\x64" },
+ },
+
+ { // BX_KEY_HOME ( ibm 80 )
+ { "\xE0\x47" , "\xE0\xC7" },
+ { "\xE0\x6C" , "\xE0\xF0\x6C" },
+ { "\x6E" , "\xF0\x6E" },
+ },
+
+ { // BX_KEY_END ( ibm 81 )
+ { "\xE0\x4F" , "\xE0\xCF" },
+ { "\xE0\x69" , "\xE0\xF0\x69" },
+ { "\x65" , "\xF0\x65" },
+ },
+
+ { // BX_KEY_PAGE_UP ( ibm 85 )
+ { "\xE0\x49" , "\xE0\xC9" },
+ { "\xE0\x7D" , "\xE0\xF0\x7D" },
+ { "\x6F" , "\xF0\x6F" },
+ },
+
+ { // BX_KEY_PAGE_DOWN ( ibm 86 )
+ { "\xE0\x51" , "\xE0\xD1" },
+ { "\xE0\x7A" , "\xE0\xF0\x7A" },
+ { "\x6D" , "\xF0\x6D" },
+ },
+
+ { // BX_KEY_KP_ADD ( ibm 106 )
+ { "\x4E" , "\xCE" },
+ { "\x79" , "\xF0\x79" },
+ { "\x7C" , "\xF0\x7C" },
+ },
+
+ { // BX_KEY_KP_SUBTRACT ( ibm 105 )
+ { "\x4A" , "\xCA" },
+ { "\x7B" , "\xF0\x7B" },
+ { "\x84" , "\xF0\x84" },
+ },
+
+ { // BX_KEY_KP_END ( ibm 93 )
+ { "\x4F" , "\xCF" },
+ { "\x69" , "\xF0\x69" },
+ { "\x69" , "\xF0\x69" },
+ },
+
+ { // BX_KEY_KP_DOWN ( ibm 98 )
+ { "\x50" , "\xD0" },
+ { "\x72" , "\xF0\x72" },
+ { "\x72" , "\xF0\x72" },
+ },
+
+ { // BX_KEY_KP_PAGE_DOWN ( ibm 103 )
+ { "\x51" , "\xD1" },
+ { "\x7A" , "\xF0\x7A" },
+ { "\x7A" , "\xF0\x7A" },
+ },
+
+ { // BX_KEY_KP_LEFT ( ibm 92 )
+ { "\x4B" , "\xCB" },
+ { "\x6B" , "\xF0\x6B" },
+ { "\x6B" , "\xF0\x6B" },
+ },
+
+ { // BX_KEY_KP_RIGHT ( ibm 102 )
+ { "\x4D" , "\xCD" },
+ { "\x74" , "\xF0\x74" },
+ { "\x74" , "\xF0\x74" },
+ },
+
+ { // BX_KEY_KP_HOME ( ibm 91 )
+ { "\x47" , "\xC7" },
+ { "\x6C" , "\xF0\x6C" },
+ { "\x6C" , "\xF0\x6C" },
+ },
+
+ { // BX_KEY_KP_UP ( ibm 96 )
+ { "\x48" , "\xC8" },
+ { "\x75" , "\xF0\x75" },
+ { "\x75" , "\xF0\x75" },
+ },
+
+ { // BX_KEY_KP_PAGE_UP ( ibm 101 )
+ { "\x49" , "\xC9" },
+ { "\x7D" , "\xF0\x7D" },
+ { "\x7D" , "\xF0\x7D" },
+ },
+
+ { // BX_KEY_KP_INSERT ( ibm 99 )
+ { "\x52" , "\xD2" },
+ { "\x70" , "\xF0\x70" },
+ { "\x70" , "\xF0\x70" },
+ },
+
+ { // BX_KEY_KP_DELETE ( ibm 104 )
+ { "\x53" , "\xD3" },
+ { "\x71" , "\xF0\x71" },
+ { "\x71" , "\xF0\x71" },
+ },
+
+ { // BX_KEY_KP_5 ( ibm 97 )
+ { "\x4C" , "\xCC" },
+ { "\x73" , "\xF0\x73" },
+ { "\x73" , "\xF0\x73" },
+ },
+
+ { // BX_KEY_UP ( ibm 83 )
+ { "\xE0\x48" , "\xE0\xC8" },
+ { "\xE0\x75" , "\xE0\xF0\x75" },
+ { "\x63" , "\xF0\x63" },
+ },
+
+ { // BX_KEY_DOWN ( ibm 84 )
+ { "\xE0\x50" , "\xE0\xD0" },
+ { "\xE0\x72" , "\xE0\xF0\x72" },
+ { "\x60" , "\xF0\x60" },
+ },
+
+ { // BX_KEY_LEFT ( ibm 79 )
+ { "\xE0\x4B" , "\xE0\xCB" },
+ { "\xE0\x6B" , "\xE0\xF0\x6B" },
+ { "\x61" , "\xF0\x61" },
+ },
+
+ { // BX_KEY_RIGHT ( ibm 89 )
+ { "\xE0\x4D" , "\xE0\xCD" },
+ { "\xE0\x74" , "\xE0\xF0\x74" },
+ { "\x6A" , "\xF0\x6A" },
+ },
+
+ { // BX_KEY_KP_ENTER ( ibm 108 )
+ { "\xE0\x1C" , "\xE0\x9C" },
+ { "\xE0\x5A" , "\xE0\xF0\x5A" },
+ { "\x79" , "\xF0\x79" },
+ },
+
+ { // BX_KEY_KP_MULTIPLY ( ibm 100 )
+ { "\x37" , "\xB7" },
+ { "\x7C" , "\xF0\x7C" },
+ { "\x7E" , "\xF0\x7E" },
+ },
+
+ { // BX_KEY_KP_DIVIDE ( ibm 95 )
+ { "\xE0\x35" , "\xE0\xB5" },
+ { "\xE0\x4A" , "\xE0\xF0\x4A" },
+ { "\x77" , "\xF0\x77" },
+ },
+
+ { // BX_KEY_WIN_L
+ { "\xE0\x5B" , "\xE0\xDB" },
+ { "\xE0\x1F" , "\xE0\xF0\x1F" },
+ { "\x8B" , "\xF0\x8B" },
+ },
+
+ { // BX_KEY_WIN_R
+ { "\xE0\x5C" , "\xE0\xDC" },
+ { "\xE0\x27" , "\xE0\xF0\x27" },
+ { "\x8C" , "\xF0\x8C" },
+ },
+
+ { // BX_KEY_MENU
+ { "\xE0\x5D" , "\xE0\xDD" },
+ { "\xE0\x2F" , "\xE0\xF0\x2F" },
+ { "\x8D" , "\xF0\x8D" },
+ },
+
+ { // BX_KEY_ALT_SYSREQ
+ { "\x54" , "\xD4" },
+ { "\x84" , "\xF0\x84" },
+ { "\x57" , "\xF0\x57" },
+ },
+
+ { // BX_KEY_CTRL_BREAK
+ { "\xE0\x46" , "\xE0\xC6" },
+ { "\xE0\x7E" , "\xE0\xF0\x7E" },
+ { "\x62" , "\xF0\x62" },
+ },
+
+ { // BX_KEY_INT_BACK
+ { "\xE0\x6A" , "\xE0\xEA" },
+ { "\xE0\x38" , "\xE0\xF0\x38" },
+ { "\x38" , "\xF0\x38" },
+ },
+
+ { // BX_KEY_INT_FORWARD
+ { "\xE0\x69" , "\xE0\xE9" },
+ { "\xE0\x30" , "\xE0\xF0\x30" },
+ { "\x30" , "\xF0\x30" },
+ },
+
+ { // BX_KEY_INT_STOP
+ { "\xE0\x68" , "\xE0\xE8" },
+ { "\xE0\x28" , "\xE0\xF0\x28" },
+ { "\x28" , "\xF0\x28" },
+ },
+
+ { // BX_KEY_INT_MAIL
+ { "\xE0\x6C" , "\xE0\xEC" },
+ { "\xE0\x48" , "\xE0\xF0\x48" },
+ { "\x48" , "\xF0\x48" },
+ },
+
+ { // BX_KEY_INT_SEARCH
+ { "\xE0\x65" , "\xE0\xE5" },
+ { "\xE0\x10" , "\xE0\xF0\x10" },
+ { "\x10" , "\xF0\x10" },
+ },
+
+ { // BX_KEY_INT_FAV
+ { "\xE0\x66" , "\xE0\xE6" },
+ { "\xE0\x18" , "\xE0\xF0\x18" },
+ { "\x18" , "\xF0\x18" },
+ },
+
+ { // BX_KEY_INT_HOME
+ { "\xE0\x32" , "\xE0\xB2" },
+ { "\xE0\x3A" , "\xE0\xF0\x3A" },
+ { "\x97" , "\xF0\x97" },
+ },
+
+ { // BX_KEY_POWER_MYCOMP
+ { "\xE0\x6B" , "\xE0\xEB" },
+ { "\xE0\x40" , "\xE0\xF0\x40" },
+ { "\x40" , "\xF0\x40" },
+ },
+
+ { // BX_KEY_POWER_CALC
+ { "\xE0\x21" , "\xE0\xA1" },
+ { "\xE0\x2B" , "\xE0\xF0\x2B" },
+ { "\x99" , "\xF0\x99" },
+ },
+
+ { // BX_KEY_POWER_SLEEP
+ { "\xE0\x5F" , "\xE0\xDF" },
+ { "\xE0\x3F" , "\xE0\xF0\x3F" },
+ { "\x7F" , "\xF0\x7F" },
+ },
+
+ { // BX_KEY_POWER_POWER
+ { "\xE0\x5E" , "\xE0\xDE" },
+ { "\xE0\x37" , "\xE0\xF0\x37" },
+ { "" , "" },
+ },
+
+ { // BX_KEY_POWER_WAKE
+ { "\xE0\x63" , "\xE0\xE3" },
+ { "\xE0\x5E" , "\xE0\xF0\x5E" },
+ { "" , "" },
+ },
+
+};
diff --git a/tools/ioemu/iodev/scancodes.h b/tools/ioemu/iodev/scancodes.h
new file mode 100644
index 0000000000..f26491b56e
--- /dev/null
+++ b/tools/ioemu/iodev/scancodes.h
@@ -0,0 +1,31 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: scancodes.h,v 1.4 2002/10/24 21:07:51 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Translation table of the 8042
+extern unsigned char translation8042[256];
+
+typedef struct {
+ const char *make;
+ const char *brek;
+ }scancode;
+
+// Scancodes table
+extern scancode scancodes[BX_KEY_NBKEYS][3];
diff --git a/tools/ioemu/iodev/scsi_commands.h b/tools/ioemu/iodev/scsi_commands.h
new file mode 100644
index 0000000000..c516fde31c
--- /dev/null
+++ b/tools/ioemu/iodev/scsi_commands.h
@@ -0,0 +1,418 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: scsi_commands.h,v 1.3 2001/10/03 13:10:38 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+/* scsi/commands.h
+ Used only in cdrom_amigaos.cc.
+
+ Operation codes for SCSI-2 commands
+
+ 30 Nov 94 Peter Urbanec Created file
+ 10 Jan 95 Peter Urbanec Added SCSI_ prefix to all commands
+ 31 Jan 95 Peter Urbanec Released to public
+
+*/
+
+
+/* All device types */
+
+#define SCSI_CHANGE_DEFINITION 0x40
+#define SCSI_COMPARE 0x39
+#define SCSI_COPY 0x18
+#define SCSI_COPY_AND_VERIFY 0x3a
+#define SCSI_INQUIRY 0x12
+#define SCSI_LOG_SELECT 0x4c
+#define SCSI_LOG_SENSE 0x4d
+#define SCSI_MODE_SELECT_6 0x15
+#define SCSI_MODE_SELECT_10 0x55
+#define SCSI_MODE_SENSE_6 0x1a
+#define SCSI_MODE_SENSE_10 0x5a
+#define SCSI_READ_BUFFER 0x3c
+#define SCSI_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_SEND_DIAGNOSTIC 0x1d
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_WRITE_BUFFER 0x3b
+
+
+/* Direct Access devices */
+
+#define SCSI_DA_CHANGE_DEFINITION 0x40
+#define SCSI_DA_COMPARE 0x39
+#define SCSI_DA_COPY 0x18
+#define SCSI_DA_COPY_AND_VERIFY 0x3a
+#define SCSI_DA_FORMAT_UNIT 0x04
+#define SCSI_DA_INQUIRY 0x12
+#define SCSI_DA_LOCK_UNLOCK_CACHE 0x36
+#define SCSI_DA_LOG_SELECT 0x4c
+#define SCSI_DA_LOG_SENSE 0x4d
+#define SCSI_DA_MODE_SELECT_6 0x15
+#define SCSI_DA_MODE_SELECT_10 0x55
+#define SCSI_DA_MODE_SENSE_6 0x1a
+#define SCSI_DA_MODE_SENSE_10 0x5a
+#define SCSI_DA_PRE_FETCH 0x34
+#define SCSI_DA_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SCSI_DA_READ_6 0x08
+#define SCSI_DA_READ_10 0x28
+#define SCSI_DA_READ_BUFFER 0x3c
+#define SCSI_DA_READ_CAPACITY 0x25
+#define SCSI_DA_READ_DEFECT_DATA 0x37
+#define SCSI_DA_READ_LONG 0x3e
+#define SCSI_DA_REASSIGN_BLOCKS 0x07
+#define SCSI_DA_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_DA_RELEASE 0x17
+#define SCSI_DA_REQUEST_SENSE 0x03
+#define SCSI_DA_RESERVE 0x16
+#define SCSI_DA_REZERO_UNIT 0x01
+#define SCSI_DA_SEARCH_DATA_EQUAL 0x31
+#define SCSI_DA_SEARCH_DATA_HIGH 0x30
+#define SCSI_DA_SEARCH_DATA_LOW 0x32
+#define SCSI_DA_SEEK_6 0x0b
+#define SCSI_DA_SEEK_10 0x2b
+#define SCSI_DA_SEND_DIAGNOSTIC 0x1d
+#define SCSI_DA_SET_LIMITS 0x33
+#define SCSI_DA_START_STOP_UNIT 0x1b
+#define SCSI_DA_SYNCHRONIZE_CACHE 0x35
+#define SCSI_DA_TEST_UNIT_READY 0x00
+#define SCSI_DA_VERIFY 0x2f
+
+
+/* Sequential access devices */
+
+#define SCSI_SA_CHANGE_DEFINITION 0x40
+#define SCSI_SA_COMPARE 0x39
+#define SCSI_SA_COPY 0x18
+#define SCSI_SA_COPY_AND_VERIFY 0x3a
+#define SCSI_SA_ERASE 0x19
+#define SCSI_SA_INQUIRY 0x12
+#define SCSI_SA_LOAD_UNLOAD 0x1b
+#define SCSI_SA_LOCATE 0x2b
+#define SCSI_SA_LOG_SELECT 0x4c
+#define SCSI_SA_LOG_SENSE 0x4d
+#define SCSI_SA_MODE_SELECT_6 0x15
+#define SCSI_SA_MODE_SELECT_10 0x55
+#define SCSI_SA_MODE_SENSE_6 0x1a
+#define SCSI_SA_MODE_SENSE_10 0x5a
+#define SCSI_SA_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SCSI_SA_READ 0x08
+#define SCSI_SA_READ_BLOCK_LIMITS 0x05
+#define SCSI_SA_READ_BUFFER 0x3c
+#define SCSI_SA_READ_POSITION 0x34
+#define SCSI_SA_READ_REVERSE 0x0f
+#define SCSI_SA_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_SA_RECOVER_BUFFERED_DATA 0x14
+#define SCSI_SA_RELEASE_UNIT 0x17
+#define SCSI_SA_REQUEST_SENSE 0x03
+#define SCSI_SA_RESERVE_UNIT 0x16
+#define SCSI_SA_REWIND 0x01
+#define SCSI_SA_SEND_DIAGNOSTIC 0x1d
+#define SCSI_SA_SPACE 0x11
+#define SCSI_SA_TEST_UNIT_READY 0x00
+#define SCSI_SA_VERIFY 0x13
+#define SCSI_SA_WRITE 0x0a
+#define SCSI_SA_WRITE_BUFFER 0x3b
+#define SCSI_SA_WRITE_FILEMARKS 0x10
+
+
+/* Printer devices */
+
+#define SCSI_PRT_CHANGE_DEFINITION 0x40
+#define SCSI_PRT_COMPARE 0x39
+#define SCSI_PRT_COPY 0x18
+#define SCSI_PRT_COPY_AND_VERIFY 0x3a
+#define SCSI_PRT_FORMAT 0x04
+#define SCSI_PRT_INQUIRY 0x12
+#define SCSI_PRT_LOG_SELECT 0x4c
+#define SCSI_PRT_LOG_SENSE 0x4d
+#define SCSI_PRT_MODE_SELECT_6 0x15
+#define SCSI_PRT_MODE_SELECT_10 0x55
+#define SCSI_PRT_MODE_SENSE_6 0x1a
+#define SCSI_PRT_MODE_SENSE_10 0x5a
+#define SCSI_PRT_PRINT 0x0a
+#define SCSI_PRT_READ_BUFFER 0x3c
+#define SCSI_PRT_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_PRT_RECOVER_BUFFERED_DATA 0x14
+#define SCSI_PRT_RELEASE_UNIT 0x17
+#define SCSI_PRT_REQUEST_SENSE 0x03
+#define SCSI_PRT_RESERVE_UNIT 0x16
+#define SCSI_PRT_SEND_DIAGNOSTIC 0x1d
+#define SCSI_PRT_SLEW_AND_PRINT 0x0b
+#define SCSI_PRT_STOP_PRINT 0x1b
+#define SCSI_PRT_SYNCHRONIZE_BUFFER 0x10
+#define SCSI_PRT_TEST_UNIT_READY 0x00
+#define SCSI_PRT_WRITE_BUFFER 0x3b
+
+
+/* Processor devices */
+
+#define SCSI_CPU_CHANGE_DEFINITION 0x40
+#define SCSI_CPU_COMPARE 0x39
+#define SCSI_CPU_COPY 0x18
+#define SCSI_CPU_COPY_AND_VERIFY 0x3a
+#define SCSI_CPU_INQUIRY 0x12
+#define SCSI_CPU_LOG_SELECT 0x4c
+#define SCSI_CPU_LOG_SENSE 0x4d
+#define SCSI_CPU_READ_BUFFER 0x3c
+#define SCSI_CPU_RECEIVE 0x08
+#define SCSI_CPU_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_CPU_REQUEST_SENSE 0x03
+#define SCSI_CPU_SEND 0x0a
+#define SCSI_CPU_SEND_DIAGNOSTIC 0x1d
+#define SCSI_CPU_TEST_UNIT_READY 0x00
+#define SCSI_CPU_WRITE_BUFFER 0x3b
+
+
+/* Write Once devices */
+
+#define SCSI_WO_CHANGE_DEFINITION 0x40
+#define SCSI_WO_COMPARE 0x39
+#define SCSI_WO_COPY 0x18
+#define SCSI_WO_COPY_AND_VERIFY 0x3a
+#define SCSI_WO_INQUIRY 0x12
+#define SCSI_WO_LOCK_UNLOCK_CACHE 0x36
+#define SCSI_WO_LOG_SELECT 0x4c
+#define SCSI_WO_LOG_SENSE 0x4d
+#define SCSI_WO_MEDIUM_SCAN 0x38
+#define SCSI_WO_MODE_SELECT_6 0x15
+#define SCSI_WO_MODE_SELECT_10 0x55
+#define SCSI_WO_MODE_SENSE_6 0x1a
+#define SCSI_WO_MODE_SENSE_10 0x5a
+#define SCSI_WO_PRE_FETCH 0x34
+#define SCSI_WO_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SCSI_WO_READ_6 0x08
+#define SCSI_WO_READ_10 0x28
+#define SCSI_WO_READ_12 0xa8
+#define SCSI_WO_READ_BUFFER 0x3c
+#define SCSI_WO_READ_CAPACITY 0x25
+#define SCSI_WO_READ_LONG 0x3e
+#define SCSI_WO_REASSIGN_BLOCKS 0x07
+#define SCSI_WO_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_WO_RELEASE 0x17
+#define SCSI_WO_REQUEST_SENSE 0x03
+#define SCSI_WO_RESERVE 0x16
+#define SCSI_WO_REZERO_UNIT 0x01
+#define SCSI_WO_SEARCH_DATA_EQUAL_10 0x31
+#define SCSI_WO_SEARCH_DATA_EQUAL_12 0xb1
+#define SCSI_WO_SEARCH_DATA_HIGH_10 0x30
+#define SCSI_WO_SEARCH_DATA_HIGH_12 0xb0
+#define SCSI_WO_SEARCH_DATA_LOW_10 0x32
+#define SCSI_WO_SEARCH_DATA_LOW_12 0xb2
+#define SCSI_WO_SEEK_6 0x0b
+#define SCSI_WO_SEEK_10 0x2b
+#define SCSI_WO_SEND_DIAGNOSTIC 0x1d
+#define SCSI_WO_SET_LIMITS_10 0x33
+#define SCSI_WO_SET_LIMITS_12 0xb3
+#define SCSI_WO_START_STOP_UNIT 0x1b
+#define SCSI_WO_SYNCHRONIZE_CACHE 0x35
+#define SCSI_WO_TEST_UNIT_READY 0x00
+#define SCSI_WO_VERIFY_10 0x2f
+#define SCSI_WO_VERIFY_12 0xaf
+#define SCSI_WO_WRITE_6 0x0a
+#define SCSI_WO_WRITE_10 0x2a
+#define SCSI_WO_WRITE_12 0xaa
+#define SCSI_WO_WRITE_AND_VERIFY_10 0x2e
+#define SCSI_WO_WRITE_AND_VERIFY_12 0xae
+#define SCSI_WO_WRITE_BUFFER 0x3b
+#define SCSI_WO_WRITE_LONG 0x3f
+
+
+/* CD-ROM devices */
+
+#define SCSI_CD_CHANGE_DEFINITION 0x40
+#define SCSI_CD_COMPARE 0x39
+#define SCSI_CD_COPY 0x18
+#define SCSI_CD_COPY_AND_VERIFY 0x3a
+#define SCSI_CD_INQUIRY 0x12
+#define SCSI_CD_LOCK_UNLOCK_CACHE 0x36
+#define SCSI_CD_LOG_SELECT 0x4c
+#define SCSI_CD_LOG_SENSE 0x4d
+#define SCSI_CD_MODE_SELECT_6 0x15
+#define SCSI_CD_MODE_SELECT_10 0x55
+#define SCSI_CD_MODE_SENSE_6 0x1a
+#define SCSI_CD_MODE_SENSE_10 0x5a
+#define SCSI_CD_PAUSE_RESUME 0x4b
+#define SCSI_CD_PLAY_AUDIO_10 0x45
+#define SCSI_CD_PLAY_AUDIO_12 0xa5
+#define SCSI_CD_PLAY_AUDIO_MSF 0x47
+#define SCSI_CD_PLAY_AUDIO_TRACK_INDEX 0x48
+#define SCSI_CD_PLAY_TRACK_RELATIVE_10 0x49
+#define SCSI_CD_PLAY_TRACK_RELATIVE_12 0xa9
+#define SCSI_CD_PRE_FETCH 0x34
+#define SCSI_CD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SCSI_CD_READ_6 0x08
+#define SCSI_CD_READ_10 0x28
+#define SCSI_CD_READ_12 0xa8
+#define SCSI_CD_READ_BUFFER 0x3c
+#define SCSI_CD_READ_CD_ROM_CAPACITY 0x25
+#define SCSI_CD_READ_HEADER 0x44
+#define SCSI_CD_READ_LONG 0x3e
+#define SCSI_CD_READ_SUB_CHANNEL 0x42
+#define SCSI_CD_READ_TOC 0x43
+#define SCSI_CD_RECEIVE_DIAGNOSTIC_RESULT 0x1c
+#define SCSI_CD_RELEASE 0x17
+#define SCSI_CD_REQUEST_SENSE 0x03
+#define SCSI_CD_RESERVE 0x16
+#define SCSI_CD_REZERO_UNIT 0x01
+#define SCSI_CD_SEARCH_DATA_EQUAL_10 0x31
+#define SCSI_CD_SEARCH_DATA_EQUAL_12 0xb1
+#define SCSI_CD_SEARCH_DATA_HIGH_10 0x30
+#define SCSI_CD_SEARCH_DATA_HIGH_12 0xb0
+#define SCSI_CD_SEARCH_DATA_LOW_10 0x32
+#define SCSI_CD_SEARCH_DATA_LOW_12 0xb2
+#define SCSI_CD_SEEK_6 0x0b
+#define SCSI_CD_SEEK_10 0x2b
+#define SCSI_CD_SEND_DIAGNOSTIC 0x1d
+#define SCSI_CD_SET_LIMITS_10 0x33
+#define SCSI_CD_SET_LIMITS_12 0xb3
+#define SCSI_CD_START_STOP_UNIT 0x1b
+#define SCSI_CD_SYNCHRONIZE_CACHE 0x35
+#define SCSI_CD_TEST_UNIT_READY 0x00
+#define SCSI_CD_VERIFY_10 0x2f
+#define SCSI_CD_VERIFY_12 0xaf
+#define SCSI_CD_WRITE_BUFFER 0x3b
+
+
+/* Scanner devices */
+
+#define SCSI_SC_CHANGE_DEFINITION 0x40
+#define SCSI_SC_COMPARE 0x39
+#define SCSI_SC_COPY 0x18
+#define SCSI_SC_COPY_AND_VERIFY 0x3a
+#define SCSI_SC_GET_DATA_BUFFER_STATUS 0x34
+#define SCSI_SC_GET_WINDOW 0x25
+#define SCSI_SC_INQUIRY 0x12
+#define SCSI_SC_LOG_SELECT 0x4c
+#define SCSI_SC_LOG_SENSE 0x4d
+#define SCSI_SC_MODE_SELECT_6 0x15
+#define SCSI_SC_MODE_SELECT_10 0x55
+#define SCSI_SC_MODE_SENSE_6 0x1a
+#define SCSI_SC_MODE_SENSE_10 0x5a
+#define SCSI_SC_OBJECT_POSITION 0x31
+#define SCSI_SC_READ 0x28
+#define SCSI_SC_READ_BUFFER 0x3c
+#define SCSI_SC_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_SC_RELEASE_UNIT 0x17
+#define SCSI_SC_REQUEST_SENSE 0x03
+#define SCSI_SC_RESERVE_UNIT 0x16
+#define SCSI_SC_SCAN 0x1b
+#define SCSI_SC_SET_WINDOW 0x24
+#define SCSI_SC_SEND 0x2a
+#define SCSI_SC_SEND_DIAGNOSTIC 0x1d
+#define SCSI_SC_TEST_UNIT_READY 0x00
+#define SCSI_SC_WRITE_BUFFER 0x3b
+
+
+/* Optical memory devices */
+
+#define SCSI_OM_CHANGE_DEFINITION 0x40
+#define SCSI_OM_COMPARE 0x39
+#define SCSI_OM_COPY 0x18
+#define SCSI_OM_COPY_AND_VERIFY 0x3a
+#define SCSI_OM_ERASE_10 0x2c
+#define SCSI_OM_ERASE_12 0xac
+#define SCSI_OM_FORMAT_UNIT 0x04
+#define SCSI_OM_INQUIRY 0x12
+#define SCSI_OM_LOCK_UNLOCK_CACHE 0x36
+#define SCSI_OM_LOG_SELECT 0x4c
+#define SCSI_OM_LOG_SENSE 0x4d
+#define SCSI_OM_MEDIUM_SCAN 0x38
+#define SCSI_OM_MODE_SELECT_6 0x15
+#define SCSI_OM_MODE_SELECT_10 0x55
+#define SCSI_OM_MODE_SENSE_6 0x1a
+#define SCSI_OM_MODE_SENSE_10 0x5a
+#define SCSI_OM_PRE_FETCH 0x34
+#define SCSI_OM_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SCSI_OM_READ_6 0x08
+#define SCSI_OM_READ_10 0x28
+#define SCSI_OM_READ_12 0xa8
+#define SCSI_OM_READ_BUFFER 0x3c
+#define SCSI_OM_READ_CAPACITY 0x25
+#define SCSI_OM_READ_DEFECT_DATA_10 0x37
+#define SCSI_OM_READ_DEFECT_DATA_12 0xb7
+#define SCSI_OM_READ_GENERATION 0x29
+#define SCSI_OM_READ_LONG 0x3e
+#define SCSI_OM_READ_UPDATED_BLOCK 0x2d
+#define SCSI_OM_REASSIGN_BLOCKS 0x07
+#define SCSI_OM_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_OM_RELEASE 0x17
+#define SCSI_OM_REQUEST_SENSE 0x03
+#define SCSI_OM_RESERVE 0x16
+#define SCSI_OM_REZERO_UNIT 0x01
+#define SCSI_OM_SEARCH_DATA_EQUAL_10 0x31
+#define SCSI_OM_SEARCH_DATA_EQUAL_12 0xb1
+#define SCSI_OM_SEARCH_DATA_HIGH_10 0x30
+#define SCSI_OM_SEARCH_DATA_HIGH_12 0xb0
+#define SCSI_OM_SEARCH_DATA_LOW_10 0x32
+#define SCSI_OM_SEARCH_DATA_LOW_12 0xb2
+#define SCSI_OM_SEEK_6 0x0b
+#define SCSI_OM_SEEK_10 0x2b
+#define SCSI_OM_SEND_DIAGNOSTIC 0x1d
+#define SCSI_OM_SET_LIMITS_10 0x33
+#define SCSI_OM_SET_LIMITS_12 0xb3
+#define SCSI_OM_START_STOP_UNIT 0x1b
+#define SCSI_OM_SYNCHRONIZE_CACHE 0x35
+#define SCSI_OM_TEST_UNIT_READY 0x00
+#define SCSI_OM_UPDATE_BLOCK 0x3d
+#define SCSI_OM_VERIFY_10 0x2f
+#define SCSI_OM_VERIFY_12 0xaf
+#define SCSI_OM_WRITE_6 0x0a
+#define SCSI_OM_WRITE_10 0x2a
+#define SCSI_OM_WRITE_12 0xaa
+#define SCSI_OM_WRITE_AND_VERIFY_10 0x2e
+#define SCSI_OM_WRITE_AND_VERIFY_12 0xae
+#define SCSI_OM_WRITE_BUFFER 0x3b
+#define SCSI_OM_WRITE_LONG 0x3f
+
+
+/* Medium changer devices */
+
+#define SCSI_MC_CHANGE_DEFINITION 0x40
+#define SCSI_MC_EXCHANGE_MEDIUM 0xa6
+#define SCSI_MC_INITIALIZE_ELEMENT_STATUS 0x07
+#define SCSI_MC_INQUIRY 0x12
+#define SCSI_MC_LOG_SELECT 0x4c
+#define SCSI_MC_LOG_SENSE 0x4d
+#define SCSI_MC_MODE_SELECT_6 0x15
+#define SCSI_MC_MODE_SELECT_10 0x55
+#define SCSI_MC_MODE_SENSE_6 0x1a
+#define SCSI_MC_MODE_SENSE_10 0x5a
+#define SCSI_MC_MOVE_MEDIUM 0xa5
+#define SCSI_MC_POSITION_TO_ELEMENT 0x2b
+#define SCSI_MC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e
+#define SCSI_MC_READ_BUFFER 0x3c
+#define SCSI_MC_READ_ELEMENT_STATUS 0xb8
+#define SCSI_MC_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_MC_RELEASE 0x17
+#define SCSI_MC_REQUEST_VOLUME_ELEMENT_ADDRESS 0xb5
+#define SCSI_MC_REQUEST_SENSE 0x03
+#define SCSI_MC_RESERVE 0x16
+#define SCSI_MC_REZERO_UNIT 0x01
+#define SCSI_MC_SEND_DIAGNOSTIC 0x1d
+#define SCSI_MC_SEND_VOLUME_TAG 0xb6
+#define SCSI_MC_TEST_UNIT_READY 0x00
+#define SCSI_MC_WRITE_BUFFER 0x3b
+
+
+/* Communications devices */
+
+#define SCSI_COM_CHANGE_DEFINITION 0x40
+#define SCSI_COM_GET_MESSAGE_6 0x08
+#define SCSI_COM_GET_MESSAGE_10 0x28
+#define SCSI_COM_GET_MESSAGE_12 0xa8
+#define SCSI_COM_INQUIRY 0x12
+#define SCSI_COM_LOG_SELECT 0x4c
+#define SCSI_COM_LOG_SENSE 0x4d
+#define SCSI_COM_MODE_SELECT_6 0x15
+#define SCSI_COM_MODE_SELECT_10 0x55
+#define SCSI_COM_MODE_SENSE_6 0x1a
+#define SCSI_COM_MODE_SENSE_10 0x5a
+#define SCSI_COM_READ_BUFFER 0x3c
+#define SCSI_COM_RECEIVE_DIAGNOSTIC_RESULTS 0x1c
+#define SCSI_COM_REQUEST_SENSE 0x03
+#define SCSI_COM_SEND_DIAGNOSTIC 0x1d
+#define SCSI_COM_SEND_MESSAGE_6 0x0a
+#define SCSI_COM_SEND_MESSAGE_10 0x2a
+#define SCSI_COM_SEND_MESSAGE_12 0xaa
+#define SCSI_COM_TEST_UNIT_READY 0x00
+#define SCSI_COM_WRITE_BUFFER 0x3b
+
diff --git a/tools/ioemu/iodev/scsidefs.h b/tools/ioemu/iodev/scsidefs.h
new file mode 100644
index 0000000000..86239e8a7d
--- /dev/null
+++ b/tools/ioemu/iodev/scsidefs.h
@@ -0,0 +1,286 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: scsidefs.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+//
+// iodev/scsidefs.h
+// $Id: scsidefs.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $
+//
+// This file was copied from ... ?
+//
+
+//***************************************************************************
+//
+// Name: SCSIDEFS.H
+//
+// Description: SCSI definitions ('C' Language)
+//
+//***************************************************************************
+
+//***************************************************************************
+// %%% TARGET STATUS VALUES %%%
+//***************************************************************************
+#define STATUS_GOOD 0x00 // Status Good
+#define STATUS_CHKCOND 0x02 // Check Condition
+#define STATUS_CONDMET 0x04 // Condition Met
+#define STATUS_BUSY 0x08 // Busy
+#define STATUS_INTERM 0x10 // Intermediate
+#define STATUS_INTCDMET 0x14 // Intermediate-condition met
+#define STATUS_RESCONF 0x18 // Reservation conflict
+#define STATUS_COMTERM 0x22 // Command Terminated
+#define STATUS_QFULL 0x28 // Queue full
+
+//***************************************************************************
+// %%% SCSI MISCELLANEOUS EQUATES %%%
+//***************************************************************************
+#define MAXLUN 7 // Maximum Logical Unit Id
+#define MAXTARG 7 // Maximum Target Id
+#define MAX_SCSI_LUNS 64 // Maximum Number of SCSI LUNs
+#define MAX_NUM_HA 8 // Maximum Number of SCSI HA's
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+//
+// %%% SCSI COMMAND OPCODES %%%
+//
+///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+//***************************************************************************
+// %%% Commands for all Device Types %%%
+//***************************************************************************
+#define SCSI_CHANGE_DEF 0x40 // Change Definition (Optional)
+#define SCSI_COMPARE 0x39 // Compare (O)
+#define SCSI_COPY 0x18 // Copy (O)
+#define SCSI_COP_VERIFY 0x3A // Copy and Verify (O)
+#define SCSI_INQUIRY 0x12 // Inquiry (MANDATORY)
+#define SCSI_LOG_SELECT 0x4C // Log Select (O)
+#define SCSI_LOG_SENSE 0x4D // Log Sense (O)
+#define SCSI_MODE_SEL6 0x15 // Mode Select 6-byte (Device Specific)
+#define SCSI_MODE_SEL10 0x55 // Mode Select 10-byte (Device Specific)
+#define SCSI_MODE_SEN6 0x1A // Mode Sense 6-byte (Device Specific)
+#define SCSI_MODE_SEN10 0x5A // Mode Sense 10-byte (Device Specific)
+#define SCSI_READ_BUFF 0x3C // Read Buffer (O)
+#define SCSI_REQ_SENSE 0x03 // Request Sense (MANDATORY)
+#define SCSI_SEND_DIAG 0x1D // Send Diagnostic (O)
+#define SCSI_TST_U_RDY 0x00 // Test Unit Ready (MANDATORY)
+#define SCSI_WRITE_BUFF 0x3B // Write Buffer (O)
+
+//***************************************************************************
+// %%% Commands Unique to Direct Access Devices %%%
+//***************************************************************************
+#define SCSI_COMPARE 0x39 // Compare (O)
+#define SCSI_FORMAT 0x04 // Format Unit (MANDATORY)
+#define SCSI_LCK_UN_CAC 0x36 // Lock Unlock Cache (O)
+#define SCSI_PREFETCH 0x34 // Prefetch (O)
+#define SCSI_MED_REMOVL 0x1E // Prevent/Allow medium Removal (O)
+#define SCSI_READ6 0x08 // Read 6-byte (MANDATORY)
+#define SCSI_READ10 0x28 // Read 10-byte (MANDATORY)
+#define SCSI_RD_CAPAC 0x25 // Read Capacity (MANDATORY)
+#define SCSI_RD_DEFECT 0x37 // Read Defect Data (O)
+#define SCSI_READ_LONG 0x3E // Read Long (O)
+#define SCSI_REASS_BLK 0x07 // Reassign Blocks (O)
+#define SCSI_RCV_DIAG 0x1C // Receive Diagnostic Results (O)
+#define SCSI_RELEASE 0x17 // Release Unit (MANDATORY)
+#define SCSI_REZERO 0x01 // Rezero Unit (O)
+#define SCSI_SRCH_DAT_E 0x31 // Search Data Equal (O)
+#define SCSI_SRCH_DAT_H 0x30 // Search Data High (O)
+#define SCSI_SRCH_DAT_L 0x32 // Search Data Low (O)
+#define SCSI_SEEK6 0x0B // Seek 6-Byte (O)
+#define SCSI_SEEK10 0x2B // Seek 10-Byte (O)
+#define SCSI_SEND_DIAG 0x1D // Send Diagnostics (MANDATORY)
+#define SCSI_SET_LIMIT 0x33 // Set Limits (O)
+#define SCSI_START_STP 0x1B // Start/Stop Unit (O)
+#define SCSI_SYNC_CACHE 0x35 // Synchronize Cache (O)
+#define SCSI_VERIFY 0x2F // Verify (O)
+#define SCSI_WRITE6 0x0A // Write 6-Byte (MANDATORY)
+#define SCSI_WRITE10 0x2A // Write 10-Byte (MANDATORY)
+#define SCSI_WRT_VERIFY 0x2E // Write and Verify (O)
+#define SCSI_WRITE_LONG 0x3F // Write Long (O)
+#define SCSI_WRITE_SAME 0x41 // Write Same (O)
+
+//***************************************************************************
+// %%% Commands Unique to Sequential Access Devices %%%
+//***************************************************************************
+#define SCSI_ERASE 0x19 // Erase (MANDATORY)
+#define SCSI_LOAD_UN 0x1B // Load/Unload (O)
+#define SCSI_LOCATE 0x2B // Locate (O)
+#define SCSI_RD_BLK_LIM 0x05 // Read Block Limits (MANDATORY)
+#define SCSI_READ_POS 0x34 // Read Position (O)
+#define SCSI_READ_REV 0x0F // Read Reverse (O)
+#define SCSI_REC_BF_DAT 0x14 // Recover Buffer Data (O)
+#define SCSI_RESERVE 0x16 // Reserve Unit (MANDATORY)
+#define SCSI_REWIND 0x01 // Rewind (MANDATORY)
+#define SCSI_SPACE 0x11 // Space (MANDATORY)
+#define SCSI_VERIFY_T 0x13 // Verify (Tape) (O)
+#define SCSI_WRT_FILE 0x10 // Write Filemarks (MANDATORY)
+
+//***************************************************************************
+// %%% Commands Unique to Printer Devices %%%
+//***************************************************************************
+#define SCSI_PRINT 0x0A // Print (MANDATORY)
+#define SCSI_SLEW_PNT 0x0B // Slew and Print (O)
+#define SCSI_STOP_PNT 0x1B // Stop Print (O)
+#define SCSI_SYNC_BUFF 0x10 // Synchronize Buffer (O)
+
+//***************************************************************************
+// %%% Commands Unique to Processor Devices %%%
+//***************************************************************************
+#define SCSI_RECEIVE 0x08 // Receive (O)
+#define SCSI_SEND 0x0A // Send (O)
+
+//***************************************************************************
+// %%% Commands Unique to Write-Once Devices %%%
+//***************************************************************************
+#define SCSI_MEDIUM_SCN 0x38 // Medium Scan (O)
+#define SCSI_SRCHDATE10 0x31 // Search Data Equal 10-Byte (O)
+#define SCSI_SRCHDATE12 0xB1 // Search Data Equal 12-Byte (O)
+#define SCSI_SRCHDATH10 0x30 // Search Data High 10-Byte (O)
+#define SCSI_SRCHDATH12 0xB0 // Search Data High 12-Byte (O)
+#define SCSI_SRCHDATL10 0x32 // Search Data Low 10-Byte (O)
+#define SCSI_SRCHDATL12 0xB2 // Search Data Low 12-Byte (O)
+#define SCSI_SET_LIM_10 0x33 // Set Limits 10-Byte (O)
+#define SCSI_SET_LIM_12 0xB3 // Set Limits 10-Byte (O)
+#define SCSI_VERIFY10 0x2F // Verify 10-Byte (O)
+#define SCSI_VERIFY12 0xAF // Verify 12-Byte (O)
+#define SCSI_WRITE12 0xAA // Write 12-Byte (O)
+#define SCSI_WRT_VER10 0x2E // Write and Verify 10-Byte (O)
+#define SCSI_WRT_VER12 0xAE // Write and Verify 12-Byte (O)
+
+//***************************************************************************
+// %%% Commands Unique to CD-ROM Devices %%%
+//***************************************************************************
+#define SCSI_PLAYAUD_10 0x45 // Play Audio 10-Byte (O)
+#define SCSI_PLAYAUD_12 0xA5 // Play Audio 12-Byte 12-Byte (O)
+#define SCSI_PLAYAUDMSF 0x47 // Play Audio MSF (O)
+#define SCSI_PLAYA_TKIN 0x48 // Play Audio Track/Index (O)
+#define SCSI_PLYTKREL10 0x49 // Play Track Relative 10-Byte (O)
+#define SCSI_PLYTKREL12 0xA9 // Play Track Relative 12-Byte (O)
+#define SCSI_READCDCAP 0x25 // Read CD-ROM Capacity (MANDATORY)
+#define SCSI_READHEADER 0x44 // Read Header (O)
+#define SCSI_SUBCHANNEL 0x42 // Read Subchannel (O)
+#define SCSI_READ_TOC 0x43 // Read TOC (O)
+
+//***************************************************************************
+// %%% Commands Unique to Scanner Devices %%%
+//***************************************************************************
+#define SCSI_GETDBSTAT 0x34 // Get Data Buffer Status (O)
+#define SCSI_GETWINDOW 0x25 // Get Window (O)
+#define SCSI_OBJECTPOS 0x31 // Object Postion (O)
+#define SCSI_SCAN 0x1B // Scan (O)
+#define SCSI_SETWINDOW 0x24 // Set Window (MANDATORY)
+
+//***************************************************************************
+// %%% Commands Unique to Optical Memory Devices %%%
+//***************************************************************************
+#define SCSI_UpdateBlk 0x3D // Update Block (O)
+
+//***************************************************************************
+// %%% Commands Unique to Medium Changer Devices %%%
+//***************************************************************************
+#define SCSI_EXCHMEDIUM 0xA6 // Exchange Medium (O)
+#define SCSI_INITELSTAT 0x07 // Initialize Element Status (O)
+#define SCSI_POSTOELEM 0x2B // Position to Element (O)
+#define SCSI_REQ_VE_ADD 0xB5 // Request Volume Element Address (O)
+#define SCSI_SENDVOLTAG 0xB6 // Send Volume Tag (O)
+
+//***************************************************************************
+// %%% Commands Unique to Communication Devices %%%
+//***************************************************************************
+#define SCSI_GET_MSG_6 0x08 // Get Message 6-Byte (MANDATORY)
+#define SCSI_GET_MSG_10 0x28 // Get Message 10-Byte (O)
+#define SCSI_GET_MSG_12 0xA8 // Get Message 12-Byte (O)
+#define SCSI_SND_MSG_6 0x0A // Send Message 6-Byte (MANDATORY)
+#define SCSI_SND_MSG_10 0x2A // Send Message 10-Byte (O)
+#define SCSI_SND_MSG_12 0xAA // Send Message 12-Byte (O)
+
+//\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+//
+// %%% END OF SCSI COMMAND OPCODES %%%
+//
+///\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
+
+//***************************************************************************
+// %%% Request Sense Data Format %%%
+//***************************************************************************
+typedef struct {
+
+ BYTE ErrorCode; // Error Code (70H or 71H)
+ BYTE SegmentNum; // Number of current segment descriptor
+ BYTE SenseKey; // Sense Key(See bit definitions too)
+ BYTE InfoByte0; // Information MSB
+ BYTE InfoByte1; // Information MID
+ BYTE InfoByte2; // Information MID
+ BYTE InfoByte3; // Information LSB
+ BYTE AddSenLen; // Additional Sense Length
+ BYTE ComSpecInf0; // Command Specific Information MSB
+ BYTE ComSpecInf1; // Command Specific Information MID
+ BYTE ComSpecInf2; // Command Specific Information MID
+ BYTE ComSpecInf3; // Command Specific Information LSB
+ BYTE AddSenseCode; // Additional Sense Code
+ BYTE AddSenQual; // Additional Sense Code Qualifier
+ BYTE FieldRepUCode; // Field Replaceable Unit Code
+ BYTE SenKeySpec15; // Sense Key Specific 15th byte
+ BYTE SenKeySpec16; // Sense Key Specific 16th byte
+ BYTE SenKeySpec17; // Sense Key Specific 17th byte
+ BYTE AddSenseBytes; // Additional Sense Bytes
+
+} SENSE_DATA_FMT;
+
+//***************************************************************************
+// %%% REQUEST SENSE ERROR CODE %%%
+//***************************************************************************
+#define SERROR_CURRENT 0x70 // Current Errors
+#define SERROR_DEFERED 0x71 // Deferred Errors
+
+//***************************************************************************
+// %%% REQUEST SENSE BIT DEFINITIONS %%%
+//***************************************************************************
+#define SENSE_VALID 0x80 // Byte 0 Bit 7
+#define SENSE_FILEMRK 0x80 // Byte 2 Bit 7
+#define SENSE_EOM 0x40 // Byte 2 Bit 6
+#define SENSE_ILI 0x20 // Byte 2 Bit 5
+
+//***************************************************************************
+// %%% REQUEST SENSE SENSE KEY DEFINITIONS %%%
+//***************************************************************************
+#define KEY_NOSENSE 0x00 // No Sense
+#define KEY_RECERROR 0x01 // Recovered Error
+#define KEY_NOTREADY 0x02 // Not Ready
+#define KEY_MEDIUMERR 0x03 // Medium Error
+#define KEY_HARDERROR 0x04 // Hardware Error
+#define KEY_ILLGLREQ 0x05 // Illegal Request
+#define KEY_UNITATT 0x06 // Unit Attention
+#define KEY_DATAPROT 0x07 // Data Protect
+#define KEY_BLANKCHK 0x08 // Blank Check
+#define KEY_VENDSPEC 0x09 // Vendor Specific
+#define KEY_COPYABORT 0x0A // Copy Abort
+#define KEY_EQUAL 0x0C // Equal (Search)
+#define KEY_VOLOVRFLW 0x0D // Volume Overflow
+#define KEY_MISCOMP 0x0E // Miscompare (Search)
+#define KEY_RESERVED 0x0F // Reserved
+
+//***************************************************************************
+// %%% PERIPHERAL DEVICE TYPE DEFINITIONS %%%
+//***************************************************************************
+#define DTYPE_DASD 0x00 // Disk Device
+#define DTYPE_SEQD 0x01 // Tape Device
+#define DTYPE_PRNT 0x02 // Printer
+#define DTYPE_PROC 0x03 // Processor
+#define DTYPE_WORM 0x04 // Write-once read-multiple
+#define DTYPE_CROM 0x05 // CD-ROM device
+#define DTYPE_CDROM 0x05 // CD-ROM device
+#define DTYPE_SCAN 0x06 // Scanner device
+#define DTYPE_OPTI 0x07 // Optical memory device
+#define DTYPE_JUKE 0x08 // Medium Changer device
+#define DTYPE_COMM 0x09 // Communications device
+#define DTYPE_RESL 0x0A // Reserved (low)
+#define DTYPE_RESH 0x1E // Reserved (high)
+#define DTYPE_UNKNOWN 0x1F // Unknown or no device type
+
+//***************************************************************************
+// %%% ANSI APPROVED VERSION DEFINITIONS %%%
+//***************************************************************************
+#define ANSI_MAYBE 0x0 // Device may or may not be ANSI approved stand
+#define ANSI_SCSI1 0x1 // Device complies to ANSI X3.131-1986 (SCSI-1)
+#define ANSI_SCSI2 0x2 // Device complies to SCSI-2
+#define ANSI_RESLO 0x3 // Reserved (low)
+#define ANSI_RESHI 0x7 // Reserved (high)
diff --git a/tools/ioemu/iodev/scsipt.h b/tools/ioemu/iodev/scsipt.h
new file mode 100644
index 0000000000..b3c8508abf
--- /dev/null
+++ b/tools/ioemu/iodev/scsipt.h
@@ -0,0 +1,144 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: scsipt.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+//
+// iodev/scsipt.h
+// $Id: scsipt.h,v 1.4 2002/09/16 16:58:36 bdenney Exp $
+//
+// This file was copied from ... ?
+//
+// distilled information from various header files from Microsoft's
+// DDK for Windows NT 4.0
+//
+
+#ifndef _SCSIPT_H_INC
+#define _SCSIPT_H_INC
+
+#include <windows.h>
+
+typedef struct {
+ USHORT Length;
+ UCHAR ScsiStatus;
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+ UCHAR CdbLength;
+ UCHAR SenseInfoLength;
+ UCHAR DataIn;
+ ULONG DataTransferLength;
+ ULONG TimeOutValue;
+ ULONG DataBufferOffset;
+ ULONG SenseInfoOffset;
+ UCHAR Cdb[16];
+} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
+
+
+typedef struct {
+ USHORT Length;
+ UCHAR ScsiStatus;
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+ UCHAR CdbLength;
+ UCHAR SenseInfoLength;
+ UCHAR DataIn;
+ ULONG DataTransferLength;
+ ULONG TimeOutValue;
+ PVOID DataBuffer;
+ ULONG SenseInfoOffset;
+ UCHAR Cdb[16];
+} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT;
+
+
+typedef struct {
+ SCSI_PASS_THROUGH spt;
+ ULONG Filler;
+ UCHAR ucSenseBuf[32];
+ UCHAR ucDataBuf[512];
+} SCSI_PASS_THROUGH_WITH_BUFFERS, *PSCSI_PASS_THROUGH_WITH_BUFFERS;
+
+
+typedef struct {
+ SCSI_PASS_THROUGH_DIRECT spt;
+ ULONG Filler;
+ UCHAR ucSenseBuf[32];
+} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
+
+
+
+typedef struct {
+ UCHAR NumberOfLogicalUnits;
+ UCHAR InitiatorBusId;
+ ULONG InquiryDataOffset;
+} SCSI_BUS_DATA, *PSCSI_BUS_DATA;
+
+
+typedef struct {
+ UCHAR NumberOfBusses;
+ SCSI_BUS_DATA BusData[1];
+} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO;
+
+
+typedef struct {
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+ BOOLEAN DeviceClaimed;
+ ULONG InquiryDataLength;
+ ULONG NextInquiryDataOffset;
+ UCHAR InquiryData[1];
+} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA;
+
+
+typedef struct {
+ ULONG Length;
+ UCHAR PortNumber;
+ UCHAR PathId;
+ UCHAR TargetId;
+ UCHAR Lun;
+} SCSI_ADDRESS, *PSCSI_ADDRESS;
+
+
+/*
+ * method codes
+ */
+#define METHOD_BUFFERED 0
+#define METHOD_IN_DIRECT 1
+#define METHOD_OUT_DIRECT 2
+#define METHOD_NEITHER 3
+
+/*
+ * file access values
+ */
+#define FILE_ANY_ACCESS 0
+#define FILE_READ_ACCESS (0x0001)
+#define FILE_WRITE_ACCESS (0x0002)
+
+
+#define IOCTL_SCSI_BASE 0x00000004
+
+/*
+ * constants for DataIn member of SCSI_PASS_THROUGH* structures
+ */
+#define SCSI_IOCTL_DATA_OUT 0
+#define SCSI_IOCTL_DATA_IN 1
+#define SCSI_IOCTL_DATA_UNSPECIFIED 2
+
+/*
+ * Standard IOCTL define
+ */
+#define CTL_CODE( DevType, Function, Method, Access ) ( \
+ ((DevType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \
+)
+
+#define IOCTL_SCSI_PASS_THROUGH CTL_CODE( IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS )
+#define IOCTL_SCSI_MINIPORT CTL_CODE( IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS )
+#define IOCTL_SCSI_GET_INQUIRY_DATA CTL_CODE( IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SCSI_GET_CAPABILITIES CTL_CODE( IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define IOCTL_SCSI_PASS_THROUGH_DIRECT CTL_CODE( IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS )
+#define IOCTL_SCSI_GET_ADDRESS CTL_CODE( IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS )
+
+
+
+#endif
diff --git a/tools/ioemu/iodev/serial.cc b/tools/ioemu/iodev/serial.cc
new file mode 100644
index 0000000000..02deec3014
--- /dev/null
+++ b/tools/ioemu/iodev/serial.cc
@@ -0,0 +1,1001 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: serial.cc,v 1.41 2003/11/09 00:14:43 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Peter Grehan (grehan@iprg.nokia.com) coded the original version of this
+// serial emulation. He implemented a single 8250, and allow terminal
+// input/output to stdout on FreeBSD.
+// The current version emulates a single 16550A with FIFO. Terminal
+// input/output now works on some more platforms.
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS theSerialDevice->
+
+#if USE_RAW_SERIAL
+#include <signal.h>
+#endif
+
+#ifdef WIN32
+#ifndef __MINGW32__
+// +++
+//#include <winsock2.h>
+#include <winsock.h>
+#endif
+#endif
+
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__) || defined(__GNU__) || defined(__APPLE__)
+#define SERIAL_ENABLE
+#endif
+
+#ifdef SERIAL_ENABLE
+extern "C" {
+#include <termios.h>
+};
+#endif
+
+#ifdef SERIAL_ENABLE
+static struct termios term_orig, term_new;
+#endif
+
+static int tty_id;
+
+bx_serial_c *theSerialDevice = NULL;
+
+ int
+libserial_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theSerialDevice = new bx_serial_c ();
+ bx_devices.pluginSerialDevice = theSerialDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theSerialDevice, BX_PLUGIN_SERIAL);
+ return(0); // Success
+}
+
+ void
+libserial_LTX_plugin_fini(void)
+{
+}
+
+bx_serial_c::bx_serial_c(void)
+{
+ put("SER");
+ settype(SERLOG);
+ tty_id = -1;
+ for (int i=0; i<BX_SERIAL_MAXDEV; i++) {
+ s[i].tx_timer_index = BX_NULL_TIMER_HANDLE;
+ s[i].rx_timer_index = BX_NULL_TIMER_HANDLE;
+ s[i].fifo_timer_index = BX_NULL_TIMER_HANDLE;
+ }
+}
+
+bx_serial_c::~bx_serial_c(void)
+{
+#ifdef SERIAL_ENABLE
+ if ((bx_options.com[0].Oenabled->get ()) && (tty_id >= 0))
+ tcsetattr(tty_id, TCSAFLUSH, &term_orig);
+#endif
+ // nothing for now
+}
+
+
+ void
+bx_serial_c::init(void)
+{
+ Bit16u ports[BX_SERIAL_MAXDEV] = {0x03f8, 0x02f8, 0x03e8, 0x02e8};
+ char name[16];
+
+ if (!bx_options.com[0].Oenabled->get ())
+ return;
+
+#ifdef SERIAL_ENABLE
+ if (strlen(bx_options.com[0].Odev->getptr ()) > 0) {
+ tty_id = open(bx_options.com[0].Odev->getptr (), O_RDWR|O_NONBLOCK,600);
+ if (tty_id < 0)
+ BX_PANIC(("open of %s (%s) failed\n",
+ "com1", bx_options.com[0].Odev->getptr ()));
+ BX_DEBUG(("tty_id: %d",tty_id));
+ tcgetattr(tty_id, &term_orig);
+ bcopy((caddr_t) &term_orig, (caddr_t) &term_new, sizeof(struct termios));
+ cfmakeraw(&term_new);
+ term_new.c_oflag |= OPOST | ONLCR; // Enable NL to CR-NL translation
+#ifndef TRUE_CTLC
+ // ctl-C will exit Bochs, or trap to the debugger
+ term_new.c_iflag &= ~IGNBRK;
+ term_new.c_iflag |= BRKINT;
+ term_new.c_lflag |= ISIG;
+#else
+ // ctl-C will be delivered to the serial port
+ term_new.c_iflag |= IGNBRK;
+ term_new.c_iflag &= ~BRKINT;
+#endif /* !def TRUE_CTLC */
+ term_new.c_iflag = 0;
+ term_new.c_oflag = 0;
+ term_new.c_cflag = CS8|CREAD|CLOCAL;
+ term_new.c_lflag = 0;
+ term_new.c_cc[VMIN] = 1;
+ term_new.c_cc[VTIME] = 0;
+ //term_new.c_iflag |= IXOFF;
+ tcsetattr(tty_id, TCSAFLUSH, &term_new);
+ }
+#endif /* def SERIAL_ENABLE */
+ // nothing for now
+#if USE_RAW_SERIAL
+ this->raw = new serial_raw("/dev/cua0", SIGUSR1);
+#endif // USE_RAW_SERIAL
+
+ /*
+ * Put the UART registers into their RESET state
+ */
+ for (unsigned i=0; i<BX_N_SERIAL_PORTS; i++) {
+ if (bx_options.com[i].Oenabled->get ()) {
+ sprintf(name, "Serial Port %d", i + 1);
+ /* serial interrupt */
+ BX_SER_THIS s[i].IRQ = 4 - (i & 1);
+ if (i < 2) {
+ DEV_register_irq(BX_SER_THIS s[i].IRQ, name);
+ }
+ /* internal state */
+ BX_SER_THIS s[i].ls_ipending = 0;
+ BX_SER_THIS s[i].ms_ipending = 0;
+ BX_SER_THIS s[i].rx_ipending = 0;
+ BX_SER_THIS s[i].fifo_ipending = 0;
+ BX_SER_THIS s[i].ls_interrupt = 0;
+ BX_SER_THIS s[i].ms_interrupt = 0;
+ BX_SER_THIS s[i].rx_interrupt = 0;
+ BX_SER_THIS s[i].tx_interrupt = 0;
+ BX_SER_THIS s[i].fifo_interrupt = 0;
+
+ if (BX_SER_THIS s[i].tx_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_SER_THIS s[i].tx_timer_index =
+ bx_pc_system.register_timer(this, tx_timer_handler, 0,
+ 0,0, "serial.tx"); // one-shot, inactive
+ }
+
+ if (BX_SER_THIS s[i].rx_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_SER_THIS s[i].rx_timer_index =
+ bx_pc_system.register_timer(this, rx_timer_handler, 0,
+ 0,0, "serial.rx"); // one-shot, inactive
+ }
+ if (BX_SER_THIS s[i].fifo_timer_index == BX_NULL_TIMER_HANDLE) {
+ BX_SER_THIS s[i].fifo_timer_index =
+ bx_pc_system.register_timer(this, fifo_timer_handler, 0,
+ 0,0, "serial.fifo"); // one-shot, inactive
+ }
+ BX_SER_THIS s[i].rx_pollstate = BX_SER_RXIDLE;
+
+ /* int enable: b0000 0000 */
+ BX_SER_THIS s[i].int_enable.rxdata_enable = 0;
+ BX_SER_THIS s[i].int_enable.txhold_enable = 0;
+ BX_SER_THIS s[i].int_enable.rxlstat_enable = 0;
+ BX_SER_THIS s[i].int_enable.modstat_enable = 0;
+
+ /* int ID: b0000 0001 */
+ BX_SER_THIS s[i].int_ident.ipending = 1;
+ BX_SER_THIS s[i].int_ident.int_ID = 0;
+
+ /* FIFO control: b0000 0000 */
+ BX_SER_THIS s[i].fifo_cntl.enable = 0;
+ BX_SER_THIS s[i].fifo_cntl.rxtrigger = 0;
+ BX_SER_THIS s[i].rx_fifo_end = 0;
+ BX_SER_THIS s[i].tx_fifo_end = 0;
+
+ /* Line Control reg: b0000 0000 */
+ BX_SER_THIS s[i].line_cntl.wordlen_sel = 0;
+ BX_SER_THIS s[i].line_cntl.stopbits = 0;
+ BX_SER_THIS s[i].line_cntl.parity_enable = 0;
+ BX_SER_THIS s[i].line_cntl.evenparity_sel = 0;
+ BX_SER_THIS s[i].line_cntl.stick_parity = 0;
+ BX_SER_THIS s[i].line_cntl.break_cntl = 0;
+ BX_SER_THIS s[i].line_cntl.dlab = 0;
+
+ /* Modem Control reg: b0000 0000 */
+ BX_SER_THIS s[i].modem_cntl.dtr = 0;
+ BX_SER_THIS s[i].modem_cntl.rts = 0;
+ BX_SER_THIS s[i].modem_cntl.out1 = 0;
+ BX_SER_THIS s[i].modem_cntl.out2 = 0;
+ BX_SER_THIS s[i].modem_cntl.local_loopback = 0;
+
+ /* Line Status register: b0110 0000 */
+ BX_SER_THIS s[i].line_status.rxdata_ready = 0;
+ BX_SER_THIS s[i].line_status.overrun_error = 0;
+ BX_SER_THIS s[i].line_status.parity_error = 0;
+ BX_SER_THIS s[i].line_status.framing_error = 0;
+ BX_SER_THIS s[i].line_status.break_int = 0;
+ BX_SER_THIS s[i].line_status.thr_empty = 1;
+ BX_SER_THIS s[i].line_status.tsr_empty = 1;
+ BX_SER_THIS s[i].line_status.fifo_error = 0;
+
+ /* Modem Status register: bXXXX 0000 */
+ BX_SER_THIS s[i].modem_status.delta_cts = 0;
+ BX_SER_THIS s[i].modem_status.delta_dsr = 0;
+ BX_SER_THIS s[i].modem_status.ri_trailedge = 0;
+ BX_SER_THIS s[i].modem_status.delta_dcd = 0;
+ BX_SER_THIS s[i].modem_status.cts = 0;
+ BX_SER_THIS s[i].modem_status.dsr = 0;
+ BX_SER_THIS s[i].modem_status.ri = 0;
+ BX_SER_THIS s[i].modem_status.dcd = 0;
+
+ BX_SER_THIS s[i].scratch = 0; /* scratch register */
+ BX_SER_THIS s[i].divisor_lsb = 1; /* divisor-lsb register */
+ BX_SER_THIS s[i].divisor_msb = 0; /* divisor-msb register */
+
+ BX_SER_THIS s[i].baudrate = 115200;
+
+ for (unsigned addr=ports[i]; addr<(unsigned)(ports[i]+8); addr++) {
+ BX_DEBUG(("register read/write: 0x%04x",addr));
+ DEV_register_ioread_handler(this, read_handler, addr, name, 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, name, 1);
+ }
+ BX_INFO(("com%d at 0x%04x irq %d", i+1, ports[i], BX_SER_THIS s[i].IRQ));
+ }
+ }
+}
+
+ void
+bx_serial_c::reset(unsigned type)
+{
+}
+
+ void
+bx_serial_c::lower_interrupt(Bit8u port)
+{
+ /* If there are no more ints pending, clear the irq */
+ if ((BX_SER_THIS s[port].rx_interrupt == 0) &&
+ (BX_SER_THIS s[port].tx_interrupt == 0) &&
+ (BX_SER_THIS s[port].ls_interrupt == 0) &&
+ (BX_SER_THIS s[port].ms_interrupt == 0) &&
+ (BX_SER_THIS s[port].fifo_interrupt == 0)) {
+ DEV_pic_lower_irq(BX_SER_THIS s[port].IRQ);
+ }
+}
+
+ void
+bx_serial_c::raise_interrupt(Bit8u port, int type)
+{
+ bx_bool gen_int = 0;
+
+ switch (type) {
+ case BX_SER_INT_IER: /* IER has changed */
+ gen_int = 1;
+ break;
+ case BX_SER_INT_RXDATA:
+ if (BX_SER_THIS s[port].int_enable.rxdata_enable) {
+ BX_SER_THIS s[port].rx_interrupt = 1;
+ gen_int = 1;
+ } else {
+ BX_SER_THIS s[port].rx_ipending = 1;
+ }
+ break;
+ case BX_SER_INT_TXHOLD:
+ if (BX_SER_THIS s[port].int_enable.txhold_enable) {
+ BX_SER_THIS s[port].tx_interrupt = 1;
+ gen_int = 1;
+ }
+ break;
+ case BX_SER_INT_RXLSTAT:
+ if (BX_SER_THIS s[port].int_enable.rxlstat_enable) {
+ BX_SER_THIS s[port].ls_interrupt = 1;
+ gen_int = 1;
+ } else {
+ BX_SER_THIS s[port].ls_ipending = 1;
+ }
+ break;
+ case BX_SER_INT_MODSTAT:
+ if ((BX_SER_THIS s[port].ms_ipending == 1) &&
+ (BX_SER_THIS s[port].int_enable.modstat_enable == 1)) {
+ BX_SER_THIS s[port].ms_interrupt = 1;
+ BX_SER_THIS s[port].ms_ipending = 0;
+ gen_int = 1;
+ }
+ break;
+ case BX_SER_INT_FIFO:
+ if (BX_SER_THIS s[port].int_enable.rxdata_enable) {
+ BX_SER_THIS s[port].fifo_interrupt = 1;
+ gen_int = 1;
+ } else {
+ BX_SER_THIS s[port].fifo_ipending = 1;
+ }
+ break;
+ }
+ if (gen_int && BX_SER_THIS s[port].modem_cntl.out2) {
+ DEV_pic_raise_irq(BX_SER_THIS s[port].IRQ);
+ }
+}
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_serial_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_SER_SMF
+ bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_serial_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_SER_SMF
+ //UNUSED(address);
+ Bit8u val;
+
+ /* SERIAL PORT 1 */
+
+ BX_DEBUG(("register read from address 0x%04x - ", (unsigned) address));
+
+ switch (address) {
+ case 0x03F8: /* receive buffer, or divisor latch LSB if DLAB set */
+ if (BX_SER_THIS s[0].line_cntl.dlab) {
+ val = BX_SER_THIS s[0].divisor_lsb;
+ } else {
+ if (BX_SER_THIS s[0].fifo_cntl.enable) {
+ val = BX_SER_THIS s[0].rx_fifo[0];
+ if (BX_SER_THIS s[0].rx_fifo_end > 0) {
+ memcpy(&BX_SER_THIS s[0].rx_fifo[0], &BX_SER_THIS s[0].rx_fifo[1], 15);
+ BX_SER_THIS s[0].rx_fifo_end--;
+ }
+ if (BX_SER_THIS s[0].rx_fifo_end == 0) {
+ BX_SER_THIS s[0].line_status.rxdata_ready = 0;
+ BX_SER_THIS s[0].rx_interrupt = 0;
+ BX_SER_THIS s[0].rx_ipending = 0;
+ BX_SER_THIS s[0].fifo_interrupt = 0;
+ BX_SER_THIS s[0].fifo_ipending = 0;
+ lower_interrupt(0);
+ }
+ } else {
+ val = BX_SER_THIS s[0].rxbuffer;
+ BX_SER_THIS s[0].line_status.rxdata_ready = 0;
+ BX_SER_THIS s[0].rx_interrupt = 0;
+ BX_SER_THIS s[0].rx_ipending = 0;
+ lower_interrupt(0);
+ }
+ }
+ break;
+
+ case 0x03F9: /* interrupt enable register, or div. latch MSB */
+ if (BX_SER_THIS s[0].line_cntl.dlab) {
+ val = BX_SER_THIS s[0].divisor_msb;
+ } else {
+ val = BX_SER_THIS s[0].int_enable.rxdata_enable |
+ (BX_SER_THIS s[0].int_enable.txhold_enable << 1) |
+ (BX_SER_THIS s[0].int_enable.rxlstat_enable << 2) |
+ (BX_SER_THIS s[0].int_enable.modstat_enable << 3);
+ }
+ break;
+
+ case 0x03FA: /* interrupt ID register */
+ /*
+ * Set the interrupt ID based on interrupt source
+ */
+ if (BX_SER_THIS s[0].ls_interrupt) {
+ BX_SER_THIS s[0].int_ident.int_ID = 0x3;
+ BX_SER_THIS s[0].int_ident.ipending = 0;
+ } else if (BX_SER_THIS s[0].fifo_interrupt) {
+ BX_SER_THIS s[0].int_ident.int_ID = 0x6;
+ BX_SER_THIS s[0].int_ident.ipending = 0;
+ } else if (BX_SER_THIS s[0].rx_interrupt) {
+ BX_SER_THIS s[0].int_ident.int_ID = 0x2;
+ BX_SER_THIS s[0].int_ident.ipending = 0;
+ } else if (BX_SER_THIS s[0].tx_interrupt) {
+ BX_SER_THIS s[0].int_ident.int_ID = 0x1;
+ BX_SER_THIS s[0].int_ident.ipending = 0;
+ } else if (BX_SER_THIS s[0].ms_interrupt) {
+ BX_SER_THIS s[0].int_ident.int_ID = 0x0;
+ BX_SER_THIS s[0].int_ident.ipending = 0;
+ } else {
+ BX_SER_THIS s[0].int_ident.int_ID = 0x0;
+ BX_SER_THIS s[0].int_ident.ipending = 1;
+ }
+ BX_SER_THIS s[0].tx_interrupt = 0;
+ lower_interrupt(0);
+
+ val = BX_SER_THIS s[0].int_ident.ipending |
+ (BX_SER_THIS s[0].int_ident.int_ID << 1) |
+ (BX_SER_THIS s[0].fifo_cntl.enable ? 0xc0 : 0x00);
+ break;
+
+ case 0x03FB: /* Line control register */
+ val = BX_SER_THIS s[0].line_cntl.wordlen_sel |
+ (BX_SER_THIS s[0].line_cntl.stopbits << 2) |
+ (BX_SER_THIS s[0].line_cntl.parity_enable << 3) |
+ (BX_SER_THIS s[0].line_cntl.evenparity_sel << 4) |
+ (BX_SER_THIS s[0].line_cntl.stick_parity << 5) |
+ (BX_SER_THIS s[0].line_cntl.break_cntl << 6) |
+ (BX_SER_THIS s[0].line_cntl.dlab << 7);
+ break;
+
+ case 0x03FC: /* MODEM control register */
+ val = BX_SER_THIS s[0].modem_cntl.dtr |
+ (BX_SER_THIS s[0].modem_cntl.rts << 1) |
+ (BX_SER_THIS s[0].modem_cntl.out1 << 2) |
+ (BX_SER_THIS s[0].modem_cntl.out2 << 3) |
+ (BX_SER_THIS s[0].modem_cntl.local_loopback << 4);
+ break;
+
+ case 0x03FD: /* Line status register */
+ val = BX_SER_THIS s[0].line_status.rxdata_ready |
+ (BX_SER_THIS s[0].line_status.overrun_error << 1) |
+ (BX_SER_THIS s[0].line_status.parity_error << 2) |
+ (BX_SER_THIS s[0].line_status.framing_error << 3) |
+ (BX_SER_THIS s[0].line_status.break_int << 4) |
+ (BX_SER_THIS s[0].line_status.thr_empty << 5) |
+ (BX_SER_THIS s[0].line_status.tsr_empty << 6) |
+ (BX_SER_THIS s[0].line_status.fifo_error << 7);
+ BX_SER_THIS s[0].line_status.overrun_error = 0;
+ BX_SER_THIS s[0].line_status.break_int = 0;
+ BX_SER_THIS s[0].ls_interrupt = 0;
+ BX_SER_THIS s[0].ls_ipending = 0;
+ lower_interrupt(0);
+ break;
+
+ case 0x03FE: /* MODEM status register */
+ val = BX_SER_THIS s[0].modem_status.delta_cts |
+ (BX_SER_THIS s[0].modem_status.delta_dsr << 1) |
+ (BX_SER_THIS s[0].modem_status.ri_trailedge << 2) |
+ (BX_SER_THIS s[0].modem_status.delta_dcd << 3) |
+ (BX_SER_THIS s[0].modem_status.cts << 4) |
+ (BX_SER_THIS s[0].modem_status.dsr << 5) |
+ (BX_SER_THIS s[0].modem_status.ri << 6) |
+ (BX_SER_THIS s[0].modem_status.dcd << 7);
+ BX_SER_THIS s[0].modem_status.delta_cts = 0;
+ BX_SER_THIS s[0].modem_status.delta_dsr = 0;
+ BX_SER_THIS s[0].modem_status.ri_trailedge = 0;
+ BX_SER_THIS s[0].modem_status.delta_dcd = 0;
+ BX_SER_THIS s[0].ms_interrupt = 0;
+ BX_SER_THIS s[0].ms_ipending = 0;
+ lower_interrupt(0);
+ break;
+
+ case 0x03FF: /* scratch register */
+ val = BX_SER_THIS s[0].scratch;
+ break;
+
+ default:
+ val = 0; // keep compiler happy
+ BX_PANIC(("unsupported io read from address=0x%04x!",
+ (unsigned) address));
+ break;
+ }
+
+ BX_DEBUG(("val = 0x%02x", (unsigned) val));
+
+ return(val);
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+void
+bx_serial_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_SER_SMF
+ bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+void
+bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_SER_SMF
+ bx_bool prev_cts, prev_dsr, prev_ri, prev_dcd;
+ bx_bool new_rx_ien, new_tx_ien, new_ls_ien, new_ms_ien;
+ bx_bool gen_int = 0;
+
+ /* SERIAL PORT 1 */
+
+ BX_DEBUG(("write to address: 0x%04x = 0x%02x",
+ (unsigned) address, (unsigned) value));
+
+ switch (address) {
+ case 0x03F8: /* transmit buffer, or divisor latch LSB if DLAB set */
+ if (BX_SER_THIS s[0].line_cntl.dlab) {
+ BX_SER_THIS s[0].divisor_lsb = value;
+
+ if ((value != 0) || (BX_SER_THIS s[0].divisor_msb != 0)) {
+ BX_SER_THIS s[0].baudrate = (int) (BX_PC_CLOCK_XTL /
+ (16 * ((BX_SER_THIS s[0].divisor_msb << 8) |
+ BX_SER_THIS s[0].divisor_lsb)));
+#if USE_RAW_SERIAL
+ BX_SER_THIS raw->set_baudrate(BX_SER_THIS s[0].baudrate);
+#endif // USE_RAW_SERIAL
+ }
+ } else {
+ Bit8u bitmask = 0xff >> (3 - BX_SER_THIS s[0].line_cntl.wordlen_sel);
+ if (BX_SER_THIS s[0].line_status.thr_empty) {
+ if (BX_SER_THIS s[0].fifo_cntl.enable) {
+ BX_SER_THIS s[0].tx_fifo[BX_SER_THIS s[0].tx_fifo_end++] = value & bitmask;
+ } else {
+ BX_SER_THIS s[0].thrbuffer = value & bitmask;
+ }
+ BX_SER_THIS s[0].line_status.thr_empty = 0;
+ if (BX_SER_THIS s[0].line_status.tsr_empty) {
+ if (BX_SER_THIS s[0].fifo_cntl.enable) {
+ BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].tx_fifo[0];
+ memcpy(&BX_SER_THIS s[0].tx_fifo[0], &BX_SER_THIS s[0].tx_fifo[1], 15);
+ BX_SER_THIS s[0].line_status.thr_empty = (--BX_SER_THIS s[0].tx_fifo_end == 0);
+ } else {
+ BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].thrbuffer;
+ BX_SER_THIS s[0].line_status.thr_empty = 1;
+ }
+ BX_SER_THIS s[0].line_status.tsr_empty = 0;
+ raise_interrupt(0, BX_SER_INT_TXHOLD);
+ bx_pc_system.activate_timer(BX_SER_THIS s[0].tx_timer_index,
+ (int) (1000000.0 / BX_SER_THIS s[0].baudrate *
+ (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5)),
+ 0); /* not continuous */
+ } else {
+ BX_SER_THIS s[0].tx_interrupt = 0;
+ lower_interrupt(0);
+ }
+ } else {
+ if (BX_SER_THIS s[0].fifo_cntl.enable) {
+ if (BX_SER_THIS s[0].tx_fifo_end < 16) {
+ BX_SER_THIS s[0].tx_fifo[BX_SER_THIS s[0].tx_fifo_end++] = value & bitmask;
+ } else {
+ BX_ERROR(("com1: transmit FIFO overflow"));
+ }
+ } else {
+ BX_ERROR(("write to tx hold register when not empty"));
+ }
+ }
+ }
+ break;
+
+ case 0x03F9: /* interrupt enable register, or div. latch MSB */
+ if (BX_SER_THIS s[0].line_cntl.dlab) {
+ BX_SER_THIS s[0].divisor_msb = value;
+
+ if ((value != 0) || (BX_SER_THIS s[0].divisor_lsb != 0)) {
+ BX_SER_THIS s[0].baudrate = (int) (BX_PC_CLOCK_XTL /
+ (16 * ((BX_SER_THIS s[0].divisor_msb << 8) |
+ BX_SER_THIS s[0].divisor_lsb)));
+#if USE_RAW_SERIAL
+ BX_SER_THIS raw->set_baudrate(BX_SER_THIS s[0].baudrate);
+#endif // USE_RAW_SERIAL
+ }
+ } else {
+ new_rx_ien = value & 0x01;
+ new_tx_ien = (value & 0x02) >> 1;
+ new_ls_ien = (value & 0x04) >> 2;
+ new_ms_ien = (value & 0x08) >> 3;
+ if (new_ms_ien != BX_SER_THIS s[0].int_enable.modstat_enable) {
+ BX_SER_THIS s[0].int_enable.modstat_enable = new_ms_ien;
+ if (BX_SER_THIS s[0].int_enable.modstat_enable == 1) {
+ if (BX_SER_THIS s[0].ms_ipending == 1) {
+ BX_SER_THIS s[0].ms_interrupt = 1;
+ BX_SER_THIS s[0].ms_ipending = 0;
+ gen_int = 1;
+ }
+ } else {
+ if (BX_SER_THIS s[0].ms_interrupt == 1) {
+ BX_SER_THIS s[0].ms_interrupt = 0;
+ BX_SER_THIS s[0].ms_ipending = 1;
+ lower_interrupt(0);
+ }
+ }
+ }
+ if (new_tx_ien != BX_SER_THIS s[0].int_enable.txhold_enable) {
+ BX_SER_THIS s[0].int_enable.txhold_enable = new_tx_ien;
+ if (BX_SER_THIS s[0].int_enable.txhold_enable == 1) {
+ BX_SER_THIS s[0].tx_interrupt = BX_SER_THIS s[0].line_status.thr_empty;
+ if (BX_SER_THIS s[0].tx_interrupt) gen_int = 1;
+ } else {
+ BX_SER_THIS s[0].tx_interrupt = 0;
+ lower_interrupt(0);
+ }
+ }
+ if (new_rx_ien != BX_SER_THIS s[0].int_enable.rxdata_enable) {
+ BX_SER_THIS s[0].int_enable.rxdata_enable = new_rx_ien;
+ if (BX_SER_THIS s[0].int_enable.rxdata_enable == 1) {
+ if (BX_SER_THIS s[0].fifo_ipending == 1) {
+ BX_SER_THIS s[0].fifo_interrupt = 1;
+ BX_SER_THIS s[0].fifo_ipending = 0;
+ gen_int = 1;
+ }
+ if (BX_SER_THIS s[0].rx_ipending == 1) {
+ BX_SER_THIS s[0].rx_interrupt = 1;
+ BX_SER_THIS s[0].rx_ipending = 0;
+ gen_int = 1;
+ }
+ } else {
+ if (BX_SER_THIS s[0].rx_interrupt == 1) {
+ BX_SER_THIS s[0].rx_interrupt = 0;
+ BX_SER_THIS s[0].rx_ipending = 1;
+ lower_interrupt(0);
+ }
+ if (BX_SER_THIS s[0].fifo_interrupt == 1) {
+ BX_SER_THIS s[0].fifo_interrupt = 0;
+ BX_SER_THIS s[0].fifo_ipending = 1;
+ lower_interrupt(0);
+ }
+ }
+ }
+ if (new_ls_ien != BX_SER_THIS s[0].int_enable.rxlstat_enable) {
+ BX_SER_THIS s[0].int_enable.rxlstat_enable = new_ls_ien;
+ if (BX_SER_THIS s[0].int_enable.rxlstat_enable == 1) {
+ if (BX_SER_THIS s[0].ls_ipending == 1) {
+ BX_SER_THIS s[0].ls_interrupt = 1;
+ BX_SER_THIS s[0].ls_ipending = 0;
+ gen_int = 1;
+ }
+ } else {
+ if (BX_SER_THIS s[0].ls_interrupt == 1) {
+ BX_SER_THIS s[0].ls_interrupt = 0;
+ BX_SER_THIS s[0].ls_ipending = 1;
+ lower_interrupt(0);
+ }
+ }
+ }
+ if (gen_int) raise_interrupt(0, BX_SER_INT_IER);
+ }
+ break;
+
+ case 0x03FA: /* FIFO control register */
+ if (!BX_SER_THIS s[0].fifo_cntl.enable && (value & 0x01)) {
+ BX_INFO(("FIFO enabled"));
+ BX_SER_THIS s[0].rx_fifo_end = 0;
+ BX_SER_THIS s[0].tx_fifo_end = 0;
+ }
+ BX_SER_THIS s[0].fifo_cntl.enable = value & 0x01;
+ if (value & 0x02) {
+ BX_SER_THIS s[0].rx_fifo_end = 0;
+ }
+ if (value & 0x04) {
+ BX_SER_THIS s[0].tx_fifo_end = 0;
+ }
+ BX_SER_THIS s[0].fifo_cntl.rxtrigger = (value & 0xc0) >> 6;
+ break;
+
+ case 0x03FB: /* Line control register */
+#if !USE_RAW_SERIAL
+ if ((value & 0x3) != 0x3) {
+ /* ignore this: this is set by FreeBSD when the console
+ code wants to set DLAB */
+ }
+#endif // !USE_RAW_SERIAL
+#if USE_RAW_SERIAL
+ if (BX_SER_THIS s[0].line_cntl.wordlen_sel != (value & 0x3)) {
+ BX_SER_THIS raw->set_data_bits((value & 0x3) + 5);
+ }
+ if (BX_SER_THIS s[0].line_cntl.stopbits != (value & 0x4) >> 2) {
+ BX_SER_THIS raw->set_stop_bits((value & 0x4 >> 2) ? 2 : 1);
+ }
+ if (BX_SER_THIS s[0].line_cntl.parity_enable != (value & 0x8) >> 3 ||
+ BX_SER_THIS s[0].line_cntl.evenparity_sel != (value & 0x10) >> 4 ||
+ BX_SER_THIS s[0].line_cntl.stick_parity != (value & 0x20) >> 5) {
+ if (((value & 0x20) >> 5) &&
+ ((value & 0x8) >> 3))
+ BX_PANIC(("sticky parity set and parity enabled"));
+ BX_SER_THIS raw->set_parity_mode(((value & 0x8) >> 3),
+ ((value & 0x10) >> 4) ? P_EVEN : P_ODD);
+ }
+ if (BX_SER_THIS s[0].line_cntl.break_cntl && !((value & 0x40) >> 6)) {
+ BX_SER_THIS raw->transmit(C_BREAK);
+ }
+#endif // USE_RAW_SERIAL
+
+ BX_SER_THIS s[0].line_cntl.wordlen_sel = value & 0x3;
+ /* These are ignored, but set them up so they can be read back */
+ BX_SER_THIS s[0].line_cntl.stopbits = (value & 0x4) >> 2;
+ BX_SER_THIS s[0].line_cntl.parity_enable = (value & 0x8) >> 3;
+ BX_SER_THIS s[0].line_cntl.evenparity_sel = (value & 0x10) >> 4;
+ BX_SER_THIS s[0].line_cntl.stick_parity = (value & 0x20) >> 5;
+ BX_SER_THIS s[0].line_cntl.break_cntl = (value & 0x40) >> 6;
+ /* used when doing future writes */
+ if (BX_SER_THIS s[0].line_cntl.dlab &&
+ !((value & 0x80) >> 7)) {
+ // Start the receive polling process if not already started
+ // and there is a valid baudrate.
+ if (BX_SER_THIS s[0].rx_pollstate == BX_SER_RXIDLE &&
+ BX_SER_THIS s[0].baudrate != 0) {
+ BX_SER_THIS s[0].rx_pollstate = BX_SER_RXPOLL;
+ bx_pc_system.activate_timer(BX_SER_THIS s[0].rx_timer_index,
+ (int) (1000000.0 / BX_SER_THIS s[0].baudrate *
+ (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5)),
+ 0); /* not continuous */
+ }
+ BX_DEBUG(("baud rate set - %d", BX_SER_THIS s[0].baudrate));
+ }
+ BX_SER_THIS s[0].line_cntl.dlab = (value & 0x80) >> 7;
+ break;
+
+ case 0x03FC: /* MODEM control register */
+ if ((value & 0x01) == 0) {
+#if USE_RAW_SERIAL
+ BX_SER_THIS raw->send_hangup();
+#endif
+ }
+
+ BX_SER_THIS s[0].modem_cntl.dtr = value & 0x01;
+ BX_SER_THIS s[0].modem_cntl.rts = (value & 0x02) >> 1;
+ BX_SER_THIS s[0].modem_cntl.out1 = (value & 0x04) >> 2;
+ BX_SER_THIS s[0].modem_cntl.out2 = (value & 0x08) >> 3;
+ BX_SER_THIS s[0].modem_cntl.local_loopback = (value & 0x10) >> 4;
+
+ if (BX_SER_THIS s[0].modem_cntl.local_loopback) {
+ prev_cts = BX_SER_THIS s[0].modem_status.cts;
+ prev_dsr = BX_SER_THIS s[0].modem_status.dsr;
+ prev_ri = BX_SER_THIS s[0].modem_status.ri;
+ prev_dcd = BX_SER_THIS s[0].modem_status.dcd;
+ BX_SER_THIS s[0].modem_status.cts = BX_SER_THIS s[0].modem_cntl.rts;
+ BX_SER_THIS s[0].modem_status.dsr = BX_SER_THIS s[0].modem_cntl.dtr;
+ BX_SER_THIS s[0].modem_status.ri = BX_SER_THIS s[0].modem_cntl.out1;
+ BX_SER_THIS s[0].modem_status.dcd = BX_SER_THIS s[0].modem_cntl.out2;
+ if (BX_SER_THIS s[0].modem_status.cts != prev_cts) {
+ BX_SER_THIS s[0].modem_status.delta_cts = 1;
+ BX_SER_THIS s[0].ms_ipending = 1;
+ }
+ if (BX_SER_THIS s[0].modem_status.dsr != prev_dsr) {
+ BX_SER_THIS s[0].modem_status.delta_dsr = 1;
+ BX_SER_THIS s[0].ms_ipending = 1;
+ }
+ if (BX_SER_THIS s[0].modem_status.ri != prev_ri)
+ BX_SER_THIS s[0].ms_ipending = 1;
+ if ((BX_SER_THIS s[0].modem_status.ri == 0) && (prev_ri == 1))
+ BX_SER_THIS s[0].modem_status.ri_trailedge = 1;
+ if (BX_SER_THIS s[0].modem_status.dcd != prev_dcd) {
+ BX_SER_THIS s[0].modem_status.delta_dcd = 1;
+ BX_SER_THIS s[0].ms_ipending = 1;
+ }
+ raise_interrupt(0, BX_SER_INT_MODSTAT);
+ } else {
+ /* set these to 0 for the time being */
+ BX_SER_THIS s[0].modem_status.cts = 0;
+ BX_SER_THIS s[0].modem_status.dsr = 0;
+ BX_SER_THIS s[0].modem_status.ri = 0;
+ BX_SER_THIS s[0].modem_status.dcd = 0;
+ }
+ break;
+
+ case 0x03FD: /* Line status register */
+ BX_ERROR(("write to line status register ignored"));
+ break;
+
+ case 0x03FE: /* MODEM status register */
+ BX_ERROR(("write to MODEM status register ignored"));
+ break;
+
+ case 0x03FF: /* scratch register */
+ BX_SER_THIS s[0].scratch = value;
+ break;
+
+ default:
+ BX_PANIC(("unsupported io write to address=0x%04x, value = 0x%02x!",
+ (unsigned) address, (unsigned) value));
+ break;
+ }
+}
+
+
+void
+bx_serial_c::rx_fifo_enq(Bit8u port, Bit8u data)
+{
+ bx_bool gen_int = 0;
+
+ if (BX_SER_THIS s[port].fifo_cntl.enable) {
+ if (BX_SER_THIS s[port].rx_fifo_end == 16) {
+ BX_ERROR(("com%d: receive FIFO overflow", port + 1));
+ BX_SER_THIS s[port].line_status.overrun_error = 1;
+ raise_interrupt(port, BX_SER_INT_RXLSTAT);
+ } else {
+ BX_SER_THIS s[port].rx_fifo[BX_SER_THIS s[0].rx_fifo_end++] = data;
+ switch (BX_SER_THIS s[port].fifo_cntl.rxtrigger) {
+ case 1:
+ if (BX_SER_THIS s[0].rx_fifo_end == 4) gen_int = 1;
+ break;
+ case 2:
+ if (BX_SER_THIS s[0].rx_fifo_end == 8) gen_int = 1;
+ break;
+ case 3:
+ if (BX_SER_THIS s[0].rx_fifo_end == 14) gen_int = 1;
+ break;
+ default:
+ gen_int = 1;
+ }
+ if (gen_int) {
+ bx_pc_system.deactivate_timer(BX_SER_THIS s[0].fifo_timer_index);
+ BX_SER_THIS s[port].line_status.rxdata_ready = 1;
+ raise_interrupt(port, BX_SER_INT_RXDATA);
+ } else {
+ bx_pc_system.activate_timer(BX_SER_THIS s[0].fifo_timer_index,
+ (int) (1000000.0 / BX_SER_THIS s[0].baudrate *
+ (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5) * 16),
+ 0); /* not continuous */
+ }
+ }
+ } else {
+ if (BX_SER_THIS s[port].line_status.rxdata_ready == 1) {
+ BX_ERROR(("com%d: overrun error", port + 1));
+ BX_SER_THIS s[port].line_status.overrun_error = 1;
+ raise_interrupt(port, BX_SER_INT_RXLSTAT);
+ }
+ BX_SER_THIS s[port].rxbuffer = data;
+ BX_SER_THIS s[port].line_status.rxdata_ready = 1;
+ raise_interrupt(port, BX_SER_INT_RXDATA);
+ }
+}
+
+
+void
+bx_serial_c::tx_timer_handler(void *this_ptr)
+{
+ bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;
+
+ class_ptr->tx_timer();
+}
+
+
+void
+bx_serial_c::tx_timer(void)
+{
+ bx_bool gen_int = 0;
+
+ if (BX_SER_THIS s[0].modem_cntl.local_loopback) {
+ rx_fifo_enq(0, BX_SER_THIS s[0].tsrbuffer);
+ } else {
+#if USE_RAW_SERIAL
+ if (!BX_SER_THIS raw->ready_transmit())
+ BX_PANIC(("Not ready to transmit"));
+ BX_SER_THIS raw->transmit(BX_SER_THIS s[0].tsrbuffer);
+#endif
+#if defined(SERIAL_ENABLE)
+ BX_DEBUG(("write: '%c'", BX_SER_THIS s[0].tsrbuffer));
+ if (tty_id >= 0) write(tty_id, (bx_ptr_t) & BX_SER_THIS s[0].tsrbuffer, 1);
+#endif
+ }
+
+ BX_SER_THIS s[0].line_status.tsr_empty = 1;
+ if (BX_SER_THIS s[0].fifo_cntl.enable && (BX_SER_THIS s[0].tx_fifo_end > 0)) {
+ BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].tx_fifo[0];
+ BX_SER_THIS s[0].line_status.tsr_empty = 0;
+ memcpy(&BX_SER_THIS s[0].tx_fifo[0], &BX_SER_THIS s[0].tx_fifo[1], 15);
+ gen_int = (--BX_SER_THIS s[0].tx_fifo_end == 0);
+ } else if (!BX_SER_THIS s[0].line_status.thr_empty) {
+ BX_SER_THIS s[0].tsrbuffer = BX_SER_THIS s[0].thrbuffer;
+ BX_SER_THIS s[0].line_status.tsr_empty = 0;
+ gen_int = 1;
+ }
+ if (!BX_SER_THIS s[0].line_status.tsr_empty) {
+ if (gen_int) {
+ BX_SER_THIS s[0].line_status.thr_empty = 1;
+ raise_interrupt(0, BX_SER_INT_TXHOLD);
+ }
+ bx_pc_system.activate_timer(BX_SER_THIS s[0].tx_timer_index,
+ (int) (1000000.0 / BX_SER_THIS s[0].baudrate *
+ (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5)),
+ 0); /* not continuous */
+ }
+}
+
+
+void
+bx_serial_c::rx_timer_handler(void *this_ptr)
+{
+ bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;
+
+ class_ptr->rx_timer();
+}
+
+
+void
+bx_serial_c::rx_timer(void)
+{
+#if BX_HAVE_SELECT
+#ifndef __BEOS__
+ struct timeval tval;
+ fd_set fds;
+#endif
+#endif
+ int bdrate = BX_SER_THIS s[0].baudrate / (BX_SER_THIS s[0].line_cntl.wordlen_sel + 5);
+ unsigned char chbuf = 0;
+
+#if BX_HAVE_SELECT
+#ifndef __BEOS__
+ tval.tv_sec = 0;
+ tval.tv_usec = 0;
+
+// MacOS: I'm not sure what to do with this, since I don't know
+// what an fd_set is or what FD_SET() or select() do. They aren't
+// declared in the CodeWarrior standard library headers. I'm just
+// leaving it commented out for the moment.
+
+ FD_ZERO(&fds);
+ if (tty_id >= 0) FD_SET(tty_id, &fds);
+
+ if ((BX_SER_THIS s[0].line_status.rxdata_ready == 0) ||
+ (BX_SER_THIS s[0].fifo_cntl.enable)) {
+#if USE_RAW_SERIAL
+ bx_bool rdy;
+ uint16 data;
+ if ((rdy = BX_SER_THIS raw->ready_receive())) {
+ data = BX_SER_THIS raw->receive();
+ if (data == C_BREAK) {
+ BX_DEBUG(("got BREAK"));
+ BX_SER_THIS s[0].line_status.break_int = 1;
+ rdy = 0;
+ }
+ }
+ if (rdy) {
+ chbuf = data;
+#elif defined(SERIAL_ENABLE)
+ if ((tty_id >= 0) && (select(tty_id + 1, &fds, NULL, NULL, &tval) == 1)) {
+ (void) read(tty_id, &chbuf, 1);
+ BX_DEBUG(("read: '%c'",chbuf));
+#else
+ if (0) {
+#endif
+ if (!BX_SER_THIS s[0].modem_cntl.local_loopback) {
+ rx_fifo_enq(0, chbuf);
+ }
+ } else {
+ if (!BX_SER_THIS s[0].fifo_cntl.enable) {
+ bdrate = (int) (1000000.0 / 100000); // Poll frequency is 100ms
+ }
+ }
+ } else {
+ // Poll at 4x baud rate to see if the next-char can
+ // be read
+ bdrate *= 4;
+ }
+#endif
+#endif
+
+ bx_pc_system.activate_timer(BX_SER_THIS s[0].rx_timer_index,
+ (int) (1000000.0 / bdrate),
+ 0); /* not continuous */
+}
+
+
+void
+bx_serial_c::fifo_timer_handler(void *this_ptr)
+{
+ bx_serial_c *class_ptr = (bx_serial_c *) this_ptr;
+
+ class_ptr->fifo_timer();
+}
+
+
+void
+bx_serial_c::fifo_timer(void)
+{
+ BX_SER_THIS s[0].line_status.rxdata_ready = 1;
+ raise_interrupt(0, BX_SER_INT_FIFO);
+}
diff --git a/tools/ioemu/iodev/serial.h b/tools/ioemu/iodev/serial.h
new file mode 100644
index 0000000000..00f01c976e
--- /dev/null
+++ b/tools/ioemu/iodev/serial.h
@@ -0,0 +1,193 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: serial.h,v 1.15 2003/11/16 08:21:10 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Peter Grehan (grehan@iprg.nokia.com) coded most of this
+// serial emulation.
+
+#if USE_RAW_SERIAL
+#include "serial_raw.h"
+#endif // USE_RAW_SERIAL
+
+#if BX_USE_SER_SMF
+# define BX_SER_SMF static
+# define BX_SER_THIS theSerialDevice->
+#else
+# define BX_SER_SMF
+# define BX_SER_THIS this->
+#endif
+
+#define BX_SERIAL_MAXDEV 4
+
+#define BX_PC_CLOCK_XTL 1843200.0
+
+#define BX_SER_RXIDLE 0
+#define BX_SER_RXPOLL 1
+#define BX_SER_RXWAIT 2
+
+enum {
+ BX_SER_INT_IER,
+ BX_SER_INT_RXDATA,
+ BX_SER_INT_TXHOLD,
+ BX_SER_INT_RXLSTAT,
+ BX_SER_INT_MODSTAT,
+ BX_SER_INT_FIFO
+};
+
+typedef struct {
+ /*
+ * UART internal state
+ */
+ bx_bool ls_interrupt;
+ bx_bool ms_interrupt;
+ bx_bool rx_interrupt;
+ bx_bool tx_interrupt;
+ bx_bool fifo_interrupt;
+ bx_bool ls_ipending;
+ bx_bool ms_ipending;
+ bx_bool rx_ipending;
+ bx_bool fifo_ipending;
+
+ Bit8u IRQ;
+
+ Bit8u rx_fifo_end;
+ Bit8u tx_fifo_end;
+
+ int baudrate;
+ int tx_timer_index;
+
+ int rx_pollstate;
+ int rx_timer_index;
+ int fifo_timer_index;
+
+ /*
+ * Register definitions
+ */
+ Bit8u rxbuffer; /* receiver buffer register (r/o) */
+ Bit8u thrbuffer; /* transmit holding register (w/o) */
+ /* Interrupt Enable Register */
+ struct {
+ bx_bool rxdata_enable; /* 1=enable receive data interrupts */
+ bx_bool txhold_enable; /* 1=enable tx. holding reg. empty ints */
+ bx_bool rxlstat_enable; /* 1=enable rx line status interrupts */
+ bx_bool modstat_enable; /* 1=enable modem status interrupts */
+ } int_enable;
+ /* Interrupt Identification Register (r/o) */
+ struct {
+ bx_bool ipending; /* 0=interrupt pending */
+ Bit8u int_ID; /* 3-bit interrupt ID */
+ } int_ident;
+ /* FIFO Control Register (w/o) */
+ struct {
+ bx_bool enable; /* 1=enable tx and rx FIFOs */
+ Bit8u rxtrigger; /* 2-bit code for rx fifo trigger level */
+ } fifo_cntl;
+ /* Line Control Register (r/w) */
+ struct {
+ Bit8u wordlen_sel; /* 2-bit code for char length */
+ bx_bool stopbits; /* select stop bit len */
+ bx_bool parity_enable; /* ... */
+ bx_bool evenparity_sel; /* ... */
+ bx_bool stick_parity; /* ... */
+ bx_bool break_cntl; /* 1=send break signal */
+ bx_bool dlab; /* divisor latch access bit */
+ } line_cntl;
+ /* MODEM Control Register (r/w) */
+ struct {
+ bx_bool dtr; /* DTR output value */
+ bx_bool rts; /* RTS output value */
+ bx_bool out1; /* OUTPUT1 value */
+ bx_bool out2; /* OUTPUT2 value */
+ bx_bool local_loopback; /* 1=loopback mode */
+ } modem_cntl;
+ /* Line Status Register (r/w) */
+ struct {
+ bx_bool rxdata_ready; /* 1=receiver data ready */
+ bx_bool overrun_error; /* 1=receive overrun detected */
+ bx_bool parity_error; /* 1=rx char has a bad parity bit */
+ bx_bool framing_error; /* 1=no stop bit detected for rx char */
+ bx_bool break_int; /* 1=break signal detected */
+ bx_bool thr_empty; /* 1=tx hold register (or fifo) is empty */
+ bx_bool tsr_empty; /* 1=shift reg and hold reg empty */
+ bx_bool fifo_error; /* 1=at least 1 err condition in fifo */
+ } line_status;
+ /* Modem Status Register (r/w) */
+ struct {
+ bx_bool delta_cts; /* 1=CTS changed since last read */
+ bx_bool delta_dsr; /* 1=DSR changed since last read */
+ bx_bool ri_trailedge; /* 1=RI moved from low->high */
+ bx_bool delta_dcd; /* 1=CD changed since last read */
+ bx_bool cts; /* CTS input value */
+ bx_bool dsr; /* DSR input value */
+ bx_bool ri; /* RI input value */
+ bx_bool dcd; /* DCD input value */
+ } modem_status;
+
+ Bit8u scratch; /* Scratch Register (r/w) */
+ Bit8u tsrbuffer; /* transmit shift register (internal) */
+ Bit8u rx_fifo[16]; /* receive FIFO (internal) */
+ Bit8u tx_fifo[16]; /* transmit FIFO (internal) */
+ Bit8u divisor_lsb; /* Divisor latch, least-sig. byte */
+ Bit8u divisor_msb; /* Divisor latch, most-sig. byte */
+} bx_serial_t;
+
+
+
+class bx_serial_c : public bx_devmodel_c {
+public:
+ bx_serial_c(void);
+ ~bx_serial_c(void);
+ virtual void init(void);
+ virtual void reset(unsigned type);
+#if USE_RAW_SERIAL
+ serial_raw* raw;
+#endif // USE_RAW_SERIAL
+
+private:
+ bx_serial_t s[BX_SERIAL_MAXDEV];
+
+ static void lower_interrupt(Bit8u port);
+ static void raise_interrupt(Bit8u port, int type);
+
+ static void rx_fifo_enq(Bit8u port, Bit8u data);
+
+ static void tx_timer_handler(void *);
+ BX_SER_SMF void tx_timer(void);
+
+ static void rx_timer_handler(void *);
+ BX_SER_SMF void rx_timer(void);
+
+ static void fifo_timer_handler(void *);
+ BX_SER_SMF void fifo_timer(void);
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_SER_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+ };
+
diff --git a/tools/ioemu/iodev/serial_raw.h b/tools/ioemu/iodev/serial_raw.h
new file mode 100644
index 0000000000..978c28d29b
--- /dev/null
+++ b/tools/ioemu/iodev/serial_raw.h
@@ -0,0 +1,23 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: serial_raw.h,v 1.2 2001/10/03 13:10:38 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+#include <linux/serial.h>
+
+#define P_EVEN 0
+#define P_ODD 1
+#define C_BREAK 201
+
+class serial_raw : public logfunctions {
+ public:
+ serial_raw (char *ttypath, int signal);
+ void set_baudrate (int rate);
+ void set_data_bits (int );
+ void set_stop_bits (int);
+ void set_parity_mode (int, int);
+ void transmit (int byte);
+ void send_hangup ();
+ int ready_transmit ();
+ int ready_receive ();
+ int receive ();
+};
diff --git a/tools/ioemu/iodev/slowdown_timer.cc b/tools/ioemu/iodev/slowdown_timer.cc
new file mode 100644
index 0000000000..76d8613ec8
--- /dev/null
+++ b/tools/ioemu/iodev/slowdown_timer.cc
@@ -0,0 +1,182 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: slowdown_timer.cc,v 1.17.2.1 2004/02/06 22:14:36 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+
+#include "bochs.h"
+#include <errno.h>
+
+//These need to stay printfs because they are useless in the log file.
+#define BX_SLOWDOWN_PRINTF_FEEDBACK 0
+
+#define SECINUSEC 1000000
+#define usectosec(a) ((a)/SECINUSEC)
+#define sectousec(a) ((a)*SECINUSEC)
+#define nsectousec(a) ((a)/1000)
+
+#define MSECINUSEC 1000
+#define usectomsec(a) ((a)/MSECINUSEC)
+
+#if BX_HAVE_USLEEP
+# define Qval 1000
+#else
+# define Qval SECINUSEC
+#endif
+
+#define MAXMULT 1.5
+#define REALTIME_Q SECINUSEC
+
+#define LOG_THIS bx_slowdown_timer.
+
+bx_slowdown_timer_c bx_slowdown_timer;
+
+bx_slowdown_timer_c::bx_slowdown_timer_c() {
+ put("STIMER");
+ settype(STIMERLOG);
+
+
+ s.start_time=0;
+ s.start_emulated_time=0;
+ s.timer_handle=BX_NULL_TIMER_HANDLE;
+}
+
+void
+bx_slowdown_timer_c::init(void) {
+
+ // Return early if slowdown timer not selected
+ if ( (bx_options.clock.Osync->get () != BX_CLOCK_SYNC_SLOWDOWN)
+ && (bx_options.clock.Osync->get () != BX_CLOCK_SYNC_BOTH) )
+ return;
+
+ BX_INFO(("using 'slowdown' timer synchronization method"));
+ s.MAXmultiplier=MAXMULT;
+ s.Q=Qval;
+
+ if(s.MAXmultiplier<1)
+ s.MAXmultiplier=1;
+
+ s.start_time=sectousec(time(NULL));
+ s.start_emulated_time = bx_pc_system.time_usec();
+ s.lasttime=0;
+ if (s.timer_handle == BX_NULL_TIMER_HANDLE) {
+ s.timer_handle=bx_pc_system.register_timer(this, timer_handler, 100 , 1, 1,
+ "slowdown_timer");
+ }
+ bx_pc_system.deactivate_timer(s.timer_handle);
+ bx_pc_system.activate_timer(s.timer_handle,(Bit32u)s.Q,0);
+}
+
+void
+bx_slowdown_timer_c::reset(unsigned type)
+{
+}
+
+void
+bx_slowdown_timer_c::timer_handler(void * this_ptr) {
+ bx_slowdown_timer_c * class_ptr = (bx_slowdown_timer_c *) this_ptr;
+
+ class_ptr->handle_timer();
+}
+
+void
+bx_slowdown_timer_c::handle_timer() {
+ Bit64u total_emu_time = (bx_pc_system.time_usec()) - s.start_emulated_time;
+ Bit64u wanttime = s.lasttime+s.Q;
+ Bit64u totaltime = sectousec(time(NULL)) - s.start_time;
+ Bit64u thistime=(wanttime>totaltime)?wanttime:totaltime;
+
+#if BX_SLOWDOWN_PRINTF_FEEDBACK
+ printf("Entering slowdown timer handler.\n");
+#endif
+
+ /* Decide if we're behind.
+ * Set interrupt interval accordingly. */
+ if(totaltime > total_emu_time) {
+ bx_pc_system.deactivate_timer(s.timer_handle);
+ bx_pc_system.activate_timer(s.timer_handle,
+ (Bit32u)(s.MAXmultiplier * (float)((Bit64s)s.Q)),
+ 0);
+#if BX_SLOWDOWN_PRINTF_FEEDBACK
+ printf("running at MAX speed\n");
+#endif
+ } else {
+ bx_pc_system.deactivate_timer(s.timer_handle);
+ bx_pc_system.activate_timer(s.timer_handle,s.Q,0);
+#if BX_SLOWDOWN_PRINTF_FEEDBACK
+ printf("running at NORMAL speed\n");
+#endif
+ }
+
+ /* Make sure we took at least one time quantum. */
+ /* This is a little strange. I'll try to explain.
+ * We're running bochs one second ahead of real time.
+ * this gives us a very precise division on whether
+ * we're ahead or behind the second line.
+ * Basically, here's how it works:
+ * *****|******************|***********...
+ * Time Time+1sec
+ * <^Bochs doesn't delay.
+ * ^>Bochs delays.
+ * <^Bochs runs at MAX speed.
+ * ^>Bochs runs at normal
+ */
+ if(wanttime > (totaltime+REALTIME_Q)) {
+#if BX_HAVE_USLEEP
+ usleep(s.Q);
+#elif BX_HAVE_MSLEEP
+ msleep(usectomsec(s.Q));
+#elif BX_HAVE_SLEEP
+ sleep(usectosec(s.Q));
+#else
+#error do not know have to sleep
+#endif //delay(wanttime-totaltime);
+ /*alternatively: delay(Q);
+ * This works okay because we share the delay between
+ * two time quantums.
+ */
+#if BX_SLOWDOWN_PRINTF_FEEDBACK
+ printf("DELAYING for a quantum\n");
+#endif
+ }
+ s.lasttime=thistime;
+
+ //Diagnostic info:
+#if 0
+ if(wanttime > (totaltime+REALTIME_Q)) {
+ if(totaltime > total_emu_time) {
+ printf("Solving OpenBSD problem.\n");
+ } else {
+ printf("too fast.\n");
+ }
+ } else {
+ if(totaltime > total_emu_time) {
+ printf("too slow.\n");
+ } else {
+ printf("sometimes invalid state, normally okay.\n");
+ }
+ }
+#endif // Diagnostic info
+}
+
diff --git a/tools/ioemu/iodev/slowdown_timer.h b/tools/ioemu/iodev/slowdown_timer.h
new file mode 100644
index 0000000000..3b6b153a71
--- /dev/null
+++ b/tools/ioemu/iodev/slowdown_timer.h
@@ -0,0 +1,33 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: slowdown_timer.h,v 1.8 2003/08/19 00:10:38 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+
+class bx_slowdown_timer_c : public logfunctions {
+
+private:
+ struct {
+ Bit64u start_time;
+ Bit64u start_emulated_time;
+ Bit64u lasttime;
+
+ int timer_handle;
+
+ float MAXmultiplier;
+ Bit64u Q; // (Q (in seconds))
+ } s;
+
+public:
+ bx_slowdown_timer_c();
+
+ void init(void);
+ void reset(unsigned type);
+
+ static void timer_handler(void * this_ptr);
+
+ void handle_timer();
+
+};
+
+extern bx_slowdown_timer_c bx_slowdown_timer;
+
diff --git a/tools/ioemu/iodev/soundlnx.cc b/tools/ioemu/iodev/soundlnx.cc
new file mode 100644
index 0000000000..773addc311
--- /dev/null
+++ b/tools/ioemu/iodev/soundlnx.cc
@@ -0,0 +1,227 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: soundlnx.cc,v 1.6 2002/12/24 10:12:26 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// This file (SOUNDLNX.CC) written and donated by Josef Drexler
+
+
+#include "bochs.h"
+#if (defined(linux) || defined(__FreeBSD__)) && BX_SUPPORT_SB16
+#define LOG_THIS bx_sb16.
+
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+bx_sound_linux_c::bx_sound_linux_c(bx_sb16_c *sb16)
+ :bx_sound_output_c(sb16)
+{
+ this->sb16 = sb16;
+ midi = NULL;
+ wavedevice = NULL;
+ wave = -1;
+}
+
+bx_sound_linux_c::~bx_sound_linux_c()
+{
+ // nothing for now
+}
+
+
+int bx_sound_linux_c::waveready()
+{
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_linux_c::midiready()
+{
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_linux_c::openmidioutput(char *device)
+{
+ if ( (device == NULL) || (strlen(device) < 1) )
+ return BX_SOUND_OUTPUT_ERR;
+
+ midi = fopen(device,"w");
+
+ if (midi == NULL)
+ {
+ WRITELOG( MIDILOG(2), "Couldn't open midi output device %s: %s.",
+ device, strerror(errno));
+ return BX_SOUND_OUTPUT_ERR;
+ }
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+
+int bx_sound_linux_c::sendmidicommand(int delta, int command, int length, Bit8u data[])
+{
+ UNUSED(delta);
+ // BX_PANIC(("Sendmidicommand!!");
+
+ fputc(command, midi);
+ fwrite(data, 1, length, midi);
+ fflush(midi); // to start playing immediately
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+
+int bx_sound_linux_c::closemidioutput()
+{
+ fclose(midi);
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+
+int bx_sound_linux_c::openwaveoutput(char *device)
+{
+ int length = strlen(device) + 1;
+
+ if (wavedevice != NULL)
+ delete(wavedevice);
+
+ wavedevice = new char[length];
+
+ if (wavedevice == NULL)
+ return BX_SOUND_OUTPUT_ERR;
+
+ strncpy(wavedevice, device, length);
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_linux_c::startwaveplayback(int frequency, int bits, int stereo, int format)
+{
+ int fmt, ret;
+ int signeddata = format & 1;
+
+ if ( (wavedevice == NULL) || (strlen(wavedevice) < 1) )
+ return BX_SOUND_OUTPUT_ERR;
+
+ if (wave == -1)
+ wave = open(wavedevice, O_WRONLY);
+ else
+ if ( (frequency == oldfreq) &&
+ (bits == oldbits) &&
+ (stereo == oldstereo) &&
+ (format == oldformat) )
+ return BX_SOUND_OUTPUT_OK; // nothing to do
+
+ oldfreq = frequency;
+ oldbits = bits;
+ oldstereo = stereo;
+ oldformat = format;
+
+ if (wave == -1)
+ return BX_SOUND_OUTPUT_ERR;
+
+ if (bits == 16)
+ if (signeddata == 1)
+ fmt = AFMT_S16_LE;
+ else
+ fmt = AFMT_U16_LE;
+ else if (bits == 8)
+ if (signeddata == 1)
+ fmt = AFMT_S8;
+ else
+ fmt = AFMT_U8;
+ else
+ return BX_SOUND_OUTPUT_ERR;
+
+ // set frequency etc.
+ ret = ioctl(wave, SNDCTL_DSP_RESET);
+ if (ret != 0)
+ WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_RESET): %s", strerror(errno));
+
+ /*
+ ret = ioctl(wave, SNDCTL_DSP_SETFRAGMENT, &fragment);
+ if (ret != 0)
+ WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_SETFRAGMENT, %d): %s",
+ fragment, strerror(errno));
+ */
+
+ ret = ioctl(wave, SNDCTL_DSP_SETFMT, &fmt);
+ if (ret != 0) // abort if the format is unknown, to avoid playing noise
+ {
+ WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_SETFMT, %d): %s",
+ fmt, strerror(errno));
+ return BX_SOUND_OUTPUT_ERR;
+ }
+
+ ret = ioctl(wave, SNDCTL_DSP_STEREO, &stereo);
+ if (ret != 0)
+ WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_STEREO, %d): %s",
+ stereo, strerror(errno));
+
+ ret = ioctl(wave, SNDCTL_DSP_SPEED, &frequency);
+ if (ret != 0)
+ WRITELOG( WAVELOG(4), "ioctl(SNDCTL_DSP_SPEED, %d): %s",
+ frequency, strerror(errno));
+
+ // ioctl(wave, SNDCTL_DSP_GETBLKSIZE, &fragment);
+ // WRITELOG( WAVELOG(4), "current output block size is %d", fragment);
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_linux_c::sendwavepacket(int length, Bit8u data[])
+{
+ int ret;
+
+ ret = write(wave, data, length);
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_linux_c::stopwaveplayback()
+{
+ // ioctl(wave, SNDCTL_DSP_SYNC);
+ // close(wave);
+ // wave = -1;
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_linux_c::closewaveoutput()
+{
+ if (wavedevice != NULL)
+ delete(wavedevice);
+
+ if (wave != -1)
+ {
+ close(wave);
+ wave = -1;
+ }
+
+ wavedevice = NULL;
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+#endif // defined(linux)
diff --git a/tools/ioemu/iodev/soundlnx.h b/tools/ioemu/iodev/soundlnx.h
new file mode 100644
index 0000000000..8f718b5acd
--- /dev/null
+++ b/tools/ioemu/iodev/soundlnx.h
@@ -0,0 +1,69 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: soundlnx.h,v 1.5 2002/12/24 10:12:26 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// This file (SOUNDLNX.H) written and donated by Josef Drexler
+
+
+#if (defined(linux) || defined(__FreeBSD__))
+
+#include "bochs.h"
+
+#define BX_SOUND_LINUX_BUFSIZE BX_SOUND_OUTPUT_WAVEPACKETSIZE
+
+class bx_sound_linux_c : public bx_sound_output_c {
+public:
+ bx_sound_linux_c(bx_sb16_c *sb16);
+ BX_SOUND_VIRTUAL ~bx_sound_linux_c();
+
+ // if virtual functions are used, we have to override them
+ // and define our own. Otherwise this file will just implement
+ // the original functions
+#ifdef BX_USE_SOUND_VIRTUAL
+ BX_SOUND_VIRTUAL int waveready();
+ BX_SOUND_VIRTUAL int midiready();
+
+ BX_SOUND_VIRTUAL int openmidioutput(char *device);
+ BX_SOUND_VIRTUAL int sendmidicommand(int delta, int command, int length, Bit8u data[]);
+ BX_SOUND_VIRTUAL int closemidioutput();
+
+ BX_SOUND_VIRTUAL int openwaveoutput(char *device);
+ BX_SOUND_VIRTUAL int startwaveplayback(int frequency, int bits, int stereo, int format);
+ BX_SOUND_VIRTUAL int sendwavepacket(int length, Bit8u data[]);
+ BX_SOUND_VIRTUAL int stopwaveplayback();
+ BX_SOUND_VIRTUAL int closewaveoutput();
+#endif
+
+private:
+ bx_sb16_c *sb16;
+ FILE *midi;
+ char *wavedevice;
+ int wave;
+ int bufferpos;
+ Bit8u audio_buffer[BX_SOUND_LINUX_BUFSIZE];
+ int oldfreq,oldbits,oldstereo,oldformat;
+};
+
+#endif // defined(linux)
diff --git a/tools/ioemu/iodev/soundwin.cc b/tools/ioemu/iodev/soundwin.cc
new file mode 100644
index 0000000000..b8b386cae4
--- /dev/null
+++ b/tools/ioemu/iodev/soundwin.cc
@@ -0,0 +1,521 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: soundwin.cc,v 1.13 2003/04/05 08:26:49 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// This file (SOUNDWIN.CC) written and donated by Josef Drexler
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+#if defined(WIN32) && BX_SUPPORT_SB16
+
+#define LOG_THIS bx_devices.pluginSB16Device->
+
+bx_sound_windows_c::bx_sound_windows_c(bx_sb16_c *sb16)
+ :bx_sound_output_c(sb16)
+{
+ this->sb16 = sb16;
+
+ MidiOpen = 0;
+ WaveOpen = 0;
+
+ ismidiready = 1;
+ iswaveready = 1;
+
+ // size is the total size of the midi header and buffer and the
+ // BX_SOUND_WINDOWS_NBUF wave header and buffers, all aligned
+ // on a 16-byte boundary
+
+#define ALIGN(size) ( (size + 15) & ~15 )
+
+#define size ALIGN(sizeof(MIDIHDR)) \
+ + ALIGN(sizeof(WAVEHDR)) \
+ + ALIGN(BX_SOUND_WINDOWS_MAXSYSEXLEN) * BX_SOUND_WINDOWS_NBUF \
+ + ALIGN(BX_SOUND_OUTPUT_WAVEPACKETSIZE) * BX_SOUND_WINDOWS_NBUF
+
+ DataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, size);
+ DataPointer = (Bit8u*) GlobalLock(DataHandle);
+
+ if (DataPointer == NULL)
+ BX_PANIC(("GlobalLock returned NULL-pointer"));
+
+#define NEWBUFFER(size) &(DataPointer[offset]); offset += ALIGN(size)
+
+ int offset = 0;
+ MidiHeader = (LPMIDIHDR) NEWBUFFER(sizeof(MIDIHDR));
+ MidiData = (LPSTR) NEWBUFFER(BX_SOUND_WINDOWS_MAXSYSEXLEN);
+
+ for (int bufnum=0; bufnum<BX_SOUND_WINDOWS_NBUF; bufnum++)
+ {
+ WaveHeader[bufnum] = (LPWAVEHDR) NEWBUFFER(sizeof(WAVEHDR));
+ WaveData[bufnum] = (LPSTR) NEWBUFFER(BX_SOUND_OUTPUT_WAVEPACKETSIZE);
+ }
+
+ if (offset > size)
+ BX_PANIC(("Allocated memory was too small!"));
+
+#undef size
+#undef ALIGN
+#undef NEWBUFFER
+}
+
+bx_sound_windows_c::~bx_sound_windows_c()
+{
+ GlobalUnlock(DataHandle);
+ GlobalFree(DataHandle);
+}
+
+int bx_sound_windows_c::waveready()
+{
+ if (iswaveready == 0)
+ checkwaveready();
+
+ if (iswaveready == 1)
+ return BX_SOUND_OUTPUT_OK;
+ else
+ return BX_SOUND_OUTPUT_ERR;
+}
+int bx_sound_windows_c::midiready()
+{
+ if (ismidiready == 0)
+ checkmidiready();
+
+ if (ismidiready == 1)
+ return BX_SOUND_OUTPUT_OK;
+ else
+ return BX_SOUND_OUTPUT_ERR;
+}
+
+int bx_sound_windows_c::openmidioutput(char *device)
+{
+ // could make the output device selectable,
+ // but currently only the midi mapper is supported
+ UNUSED(device);
+
+ UINT deviceid = (UINT) MIDIMAPPER;
+
+ MidiOpen = 0;
+
+ UINT ret = midiOutOpen( &MidiOut, deviceid, 0, 0, CALLBACK_NULL);
+ if (ret == 0)
+ MidiOpen = 1;
+
+ WRITELOG( MIDILOG(4), "midiOutOpen() = %d, MidiOpen: %d", ret, MidiOpen);
+
+ return (MidiOpen == 1) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR;
+}
+
+int bx_sound_windows_c::sendmidicommand(int delta, int command, int length, Bit8u data[])
+{
+ UINT ret;
+
+ if (MidiOpen != 1)
+ return BX_SOUND_OUTPUT_ERR;
+
+ if ( (command == 0xf0) || (command == 0xf7) || (length > 3) )
+ {
+ WRITELOG( WAVELOG(5), "SYSEX started, length %d", length);
+ ismidiready = 0; // until the buffer is done
+ memcpy(MidiData, data, length);
+ MidiHeader->lpData = MidiData;
+ MidiHeader->dwBufferLength = BX_SOUND_WINDOWS_MAXSYSEXLEN;
+ MidiHeader->dwBytesRecorded = 0;
+ MidiHeader->dwUser = 0;
+ MidiHeader->dwFlags = 0;
+ ret = midiOutPrepareHeader(MidiOut, MidiHeader, sizeof(*MidiHeader));
+ if (ret != 0)
+ WRITELOG( MIDILOG(2), "midiOutPrepareHeader() = %d", ret);
+ ret = midiOutLongMsg(MidiOut, MidiHeader, sizeof(*MidiHeader));
+ if (ret != 0)
+ WRITELOG( MIDILOG(2), "midiOutLongMsg() = %d", ret);
+ }
+ else
+ {
+ DWORD msg = command;
+
+ for (int i = 0; i<length; i++)
+ msg |= (data[i] << (8 * (i + 1) ) );
+
+ ret = midiOutShortMsg(MidiOut, msg);
+ WRITELOG( MIDILOG(4), "midiOutShortMsg(%x) = %d", msg, ret);
+ }
+
+ return (ret == 0) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR;
+}
+
+int bx_sound_windows_c::closemidioutput()
+{
+ UINT ret;
+
+ if (MidiOpen != 1)
+ return BX_SOUND_OUTPUT_ERR;
+
+ ret = midiOutReset(MidiOut);
+ if (ismidiready == 0)
+ checkmidiready(); // to clear any pending SYSEX
+
+ ret = midiOutClose(MidiOut);
+ WRITELOG( MIDILOG(4), "midiOutClose() = %d", ret);
+ MidiOpen = 0;
+
+ return (ret == 0) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR;
+}
+
+int bx_sound_windows_c::openwaveoutput(char *device)
+{
+ // could make the output device selectable,
+ // but currently only the midi mapper is supported
+ UNUSED(device);
+
+ WRITELOG( WAVELOG(4), "openwaveoutput(%s)", device);
+
+#ifdef usewaveOut
+ WaveDevice = (UINT) WAVEMAPPER;
+
+ for (int i=0; i<BX_SOUND_WINDOWS_NBUF; i++)
+ WaveHeader[i]->dwFlags = WHDR_DONE;
+
+ head = 0;
+ tailfull = 0;
+ tailplay = 0;
+ needreopen = 0;
+#endif
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_windows_c::playnextbuffer()
+{
+ UINT ret;
+ PCMWAVEFORMAT waveformat;
+ int bufnum;
+
+ // if the format is different, we have to reopen the device,
+ // so reset it first
+ if (needreopen != 0)
+ if (WaveOpen != 0)
+ ret = waveOutReset( WaveOut );
+
+ // clean up the buffers and mark if output is ready
+ checkwaveready();
+
+ // do we have to play anything?
+ if (tailplay == head)
+ return BX_SOUND_OUTPUT_OK;
+
+ // if the format is different, we have to close and reopen the device
+ // or, just open the device if it's not open yet
+ if ( (needreopen != 0) || (WaveOpen == 0) )
+ {
+ if (WaveOpen != 0)
+ {
+ ret = waveOutClose( WaveOut );
+ WaveOpen = 0;
+ }
+
+ // try three times to find a suitable format
+ for (int tries = 0; tries < 3; tries++)
+ {
+ int frequency = WaveInfo.frequency;
+ int stereo = WaveInfo.stereo;
+ int bits = WaveInfo.bits;
+ int format = WaveInfo.format;
+ int bps = (bits / 8) * (stereo + 1);
+
+ waveformat.wf.wFormatTag = WAVE_FORMAT_PCM;
+ waveformat.wf.nChannels = stereo + 1;
+ waveformat.wf.nSamplesPerSec = frequency;
+ waveformat.wf.nAvgBytesPerSec = frequency * bps;
+ waveformat.wf.nBlockAlign = bps;
+ waveformat.wBitsPerSample = bits;
+
+ ret = waveOutOpen( &(WaveOut), WaveDevice, (LPWAVEFORMATEX)&(waveformat.wf), 0, 0, CALLBACK_NULL);
+ if (ret != 0)
+ {
+ char errormsg[4*MAXERRORLENGTH+1];
+ waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
+ WRITELOG( WAVELOG(5), "waveOutOpen: %s", errormsg);
+ switch (tries)
+ {
+ case 0: // maybe try a different frequency
+ if (frequency < 15600)
+ frequency = 11025;
+ else if (frequency < 31200)
+ frequency = 22050;
+ else
+ frequency = 44100;
+
+ WRITELOG( WAVELOG(4), "Couldn't open wave device (error %d), trying frequency %d", ret, frequency);
+
+ break;
+ case 1: // or something else
+ frequency = 11025;
+ stereo = 0;
+ bits = 8;
+ bps = 1;
+
+ WRITELOG( WAVELOG(4), "Couldn't open wave device again (error %d), trying 11KHz, mono, 8bit", ret);
+
+ break;
+ case 2: // nope, doesn't work
+
+ WRITELOG( WAVELOG(2), "Couldn't open wave device (error %d)!", ret);
+
+ return BX_SOUND_OUTPUT_ERR;
+ }
+ WRITELOG( WAVELOG(5), "The format was: wFormatTag=%d, nChannels=%d, nSamplesPerSec=%d,",
+ waveformat.wf.wFormatTag, waveformat.wf.nChannels, waveformat.wf.nSamplesPerSec);
+ WRITELOG( WAVELOG(5), " nAvgBytesPerSec=%d, nBlockAlign=%d, wBitsPerSample=%d",
+ waveformat.wf.nAvgBytesPerSec, waveformat.wf.nBlockAlign, waveformat.wBitsPerSample);
+
+ }
+ else
+ {
+ WaveOpen = 1;
+ needreopen = 0;
+ break;
+ }
+ }
+ }
+
+ for (bufnum=tailplay; bufnum != head;
+ bufnum++, bufnum &= BX_SOUND_WINDOWS_NMASK, tailplay=bufnum)
+ {
+ WRITELOG( WAVELOG(5), "Playing buffer %d", bufnum);
+
+ // prepare the wave header
+ WaveHeader[bufnum]->lpData = WaveData[bufnum];
+ WaveHeader[bufnum]->dwBufferLength = length[bufnum];
+ WaveHeader[bufnum]->dwBytesRecorded = length[bufnum];
+ WaveHeader[bufnum]->dwUser = 0;
+ WaveHeader[bufnum]->dwFlags = 0;
+ WaveHeader[bufnum]->dwLoops = 1;
+
+ ret = waveOutPrepareHeader(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
+ if (ret != 0)
+ {
+ WRITELOG( WAVELOG(2), "waveOutPrepareHeader = %d", ret);
+ return BX_SOUND_OUTPUT_ERR;
+ }
+
+ ret = waveOutWrite(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
+ if (ret != 0)
+ {
+ char errormsg[4*MAXERRORLENGTH+1];
+ waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
+ WRITELOG( WAVELOG(5), "waveOutWrite: %s", errormsg);
+ }
+ }
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_windows_c::startwaveplayback(int frequency, int bits, int stereo, int format)
+{
+ // UINT ret;
+
+ WRITELOG( WAVELOG(4), "startwaveplayback(%d, %d, %d, %x)", frequency, bits, stereo, format);
+
+#ifdef usewaveOut
+ // check if any of the properties have changed
+ if ( (WaveInfo.frequency != frequency) ||
+ (WaveInfo.bits != bits) ||
+ (WaveInfo.stereo != stereo) ||
+ (WaveInfo.format != format) )
+ {
+ needreopen = 1;
+
+ // store the current settings to be used by sendwavepacket()
+ WaveInfo.frequency = frequency;
+ WaveInfo.bits = bits;
+ WaveInfo.stereo = stereo;
+ WaveInfo.format = format;
+ }
+#endif
+
+#ifdef usesndPlaySnd
+ int bps = (bits / 8) * (stereo + 1);
+ LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData;
+
+ memcpy(header->RIFF, "RIFF", 4);
+ memcpy(header->TYPE, "WAVE", 4);
+ memcpy(header->chnk, "fmt ", 4);
+ header->chnklen = 16;
+ header->waveformat.wf.wFormatTag = WAVE_FORMAT_PCM;
+ header->waveformat.wf.nChannels = stereo + 1;
+ header->waveformat.wf.nSamplesPerSec = frequency;
+ header->waveformat.wf.nAvgBytesPerSec = frequency * bps;
+ header->waveformat.wf.nBlockAlign = bps;
+ header->waveformat.wBitsPerSample = bits;
+ memcpy(header->chnk2, "data", 4);
+#endif
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_windows_c::sendwavepacket(int length, Bit8u data[])
+{
+// UINT ret;
+ int bufnum;
+
+ WRITELOG( WAVELOG(4), "sendwavepacket(%d, %p)", length, data);
+
+#ifdef usewaveOut
+ bufnum = head;
+
+ memcpy(WaveData[bufnum], data, length);
+ this->length[bufnum] = length;
+
+ // select next buffer to write to
+ bufnum++;
+ bufnum &= BX_SOUND_WINDOWS_NMASK;
+
+ if ( ( (bufnum + 1) & BX_SOUND_WINDOWS_NMASK) == tailfull )
+ { // this should not actually happen!
+ WRITELOG( WAVELOG(2), "Output buffer overflow! Not played. Iswaveready was %d", iswaveready);
+ iswaveready = 0; // stop the output for a while
+ return BX_SOUND_OUTPUT_ERR;
+ }
+
+ head = bufnum;
+
+ // check if more buffers are available, otherwise stall the emulator
+ if ( ( (bufnum + 2) & BX_SOUND_WINDOWS_NMASK) == tailfull )
+ {
+ WRITELOG( WAVELOG(5), "Buffer status: Head %d, TailFull %d, TailPlay %d. Stall.",
+ head, tailfull, tailplay);
+ iswaveready = 0;
+ }
+
+ playnextbuffer();
+
+#endif
+
+#ifdef usesndPlaySnd
+ LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData;
+
+ header->length = length + 36;
+ header->chnk2len = length;
+
+ memcpy( &(header->data), data, length);
+
+ FILE *test = fopen("test", "a");
+ fwrite(WaveData, 1, length + 44, test);
+ fclose(test);
+
+ ret = sndPlaySoundA( (LPCSTR) header, SND_SYNC | SND_MEMORY );
+ if (ret != 0)
+ {
+ WRITELOG( WAVELOG(3), "sndPlaySoundA: %d", ret);
+ }
+#endif
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_windows_c::stopwaveplayback()
+{
+ WRITELOG( WAVELOG(4), "stopwaveplayback()");
+
+#ifdef usewaveOut
+ // this is handled by checkwaveready() when closing
+#endif
+
+#ifdef usesndPlaySnd
+ sndPlaySoundA( NULL, SND_ASYNC | SND_MEMORY );
+
+ WaveOpen = 0;
+#endif
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+int bx_sound_windows_c::closewaveoutput()
+{
+// int bufnum;
+
+ WRITELOG( WAVELOG(4), "closewaveoutput");
+
+#ifdef usewaveOut
+ if (WaveOpen == 1)
+ {
+ waveOutReset(WaveOut);
+
+ // let checkwaveready() clean up the buffers
+ checkwaveready();
+
+ waveOutClose(WaveOut);
+
+ head = 0;
+ tailfull = 0;
+ tailplay = 0;
+ needreopen = 0;
+ }
+#endif
+
+ return BX_SOUND_OUTPUT_OK;
+}
+
+void bx_sound_windows_c::checkmidiready()
+{
+ UINT ret;
+
+ if ( (MidiHeader->dwFlags & WHDR_DONE) != 0)
+ {
+ WRITELOG( MIDILOG(5), "SYSEX message done, midi ready again.");
+ ret = midiOutUnprepareHeader( MidiOut, MidiHeader, sizeof(*MidiHeader));
+ ismidiready = 1;
+ }
+}
+void bx_sound_windows_c::checkwaveready()
+{
+ int bufnum;
+ UINT ret;
+
+ // clean up all finished buffers and mark them as available
+ for (bufnum=tailfull;
+ (bufnum != tailplay) &&
+ ( (WaveHeader[bufnum]->dwFlags & WHDR_DONE) != 0);
+ bufnum++, bufnum &= BX_SOUND_WINDOWS_NMASK)
+ {
+ WRITELOG( WAVELOG(5), "Buffer %d done.", bufnum);
+
+ ret = waveOutUnprepareHeader(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
+ }
+
+ tailfull = bufnum;
+
+ // enable gathering data if a buffer is available
+ if ( ( (head + 2) & BX_SOUND_WINDOWS_NMASK) != tailfull )
+ {
+ WRITELOG( WAVELOG(5), "Buffer status: Head %d, TailFull %d, TailPlay %d. Ready.",
+ head, tailfull, tailplay);
+ iswaveready = 1;
+ }
+}
+
+#endif // defined(WIN32)
diff --git a/tools/ioemu/iodev/soundwin.h b/tools/ioemu/iodev/soundwin.h
new file mode 100644
index 0000000000..122fa554e9
--- /dev/null
+++ b/tools/ioemu/iodev/soundwin.h
@@ -0,0 +1,229 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: soundwin.h,v 1.3 2001/10/03 13:10:38 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+// This file (SOUNDWIN.H) written and donated by Josef Drexler
+
+
+#if defined(WIN32)
+
+#include "bochs.h"
+#include <windows.h>
+
+// uncomment one of the following two #defines
+//#define usesndPlaySnd
+#define usewaveOut
+
+#define BX_SOUND_WINDOWS_MAXSYSEXLEN 256 // maximum supported length of a sysex message
+
+#define BX_SOUND_WINDOWS_NBUF 4 // number of buffers for the output, must be power of 2 and >= 4
+#define BX_SOUND_WINDOWS_NMASK (BX_SOUND_WINDOWS_NBUF - 1)
+
+#ifndef WAVEMAPPER
+#define WAVEMAPPER -1
+#endif
+
+// Definitions for WINMM.DLL, if not defined already
+#ifndef MMSYSERR_NOERROR
+
+#pragma pack(1)
+
+typedef UINT HMIDIOUT;
+typedef HMIDIOUT *LPHMIDIOUT;
+typedef struct midihdr_tag {
+ LPSTR lpData;
+ DWORD dwBufferLength;
+ DWORD dwBytesRecorded;
+ DWORD dwUser;
+ DWORD dwFlags;
+ struct midihdr_tag *lpNext;
+ DWORD reserved;
+} MIDIHDR, *LPMIDIHDR;
+
+typedef UINT HWAVEOUT;
+typedef HWAVEOUT *LPHWAVEOUT;
+
+typedef struct wavehdr_tag {
+ LPSTR lpData;
+ DWORD dwBufferLength;
+ DWORD dwBytesRecorded;
+ DWORD dwUser;
+ DWORD dwFlags;
+ DWORD dwLoops;
+ struct wavehdr_tag *lpNext;
+ DWORD reserved;
+} WAVEHDR, *LPWAVEHDR;
+
+#define WHDR_DONE 0x00000001
+#define WHDR_PREPARED 0x00000002
+#define WHDR_BEGINLOOP 0x00000004
+#define WHDR_ENDLOOP 0x00000008
+#define WHDR_INQUEUE 0x00000010
+
+
+typedef struct waveformat_tag {
+ WORD wFormatTag;
+ WORD nChannels;
+ DWORD nSamplesPerSec;
+ DWORD nAvgBytesPerSec;
+ WORD nBlockAlign;
+} WAVEFORMAT, *LPWAVEFORMAT;
+
+#define WAVE_FORMAT_PCM 1
+
+typedef struct pcmwaveformat_tag {
+ WAVEFORMAT wf;
+ WORD wBitsPerSample;
+} PCMWAVEFORMAT, *LPPCMWAVEFORMAT;
+
+#define MIDIMAPPER -1
+
+#define CALLBACK_NULL 0x00000000
+#define CALLBACK_WINDOW 0x00010000
+#define CALLBACK_TASK 0x00020000
+#define CALLBACK_FUNCTION 0x00030000
+
+#define MMSYSERR_NOERROR 0
+#define MMSYSERR_ERROR 1
+#define MMSYSERR_BADDEVICEID 2
+#define MMSYSERR_NOTENABLED 3
+#define MMSYSERR_ALLOCATED 4
+#define MMSYSERR_INVALHANDLE 5
+#define MMSYSERR_NODRIVER 6
+#define MMSYSERR_NOMEM 7
+#define MMSYSERR_NOTSUPPORTED 8
+#define MMSYSERR_NOMAP 7
+
+#define MIDIERR_UNPREPARED 64
+#define MIDIERR_STILLPLAYING 65
+#define MIDIERR_NOTREADY 66
+#define MIDIERR_NODEVICE 67
+
+#define WAVERR_BADFORMAT 32
+#define WAVERR_STILLPLAYING 33
+#define WAVERR_UNPREPARED 34
+#define WAVERR_SYNC 35
+
+#define MAXERRORLENGTH 128
+
+extern "C" {
+UINT STDCALL midiOutOpen(LPHMIDIOUT, UINT, DWORD, DWORD, DWORD);
+UINT STDCALL midiOutShortMsg(HMIDIOUT, DWORD);
+UINT STDCALL midiOutLongMsg(HMIDIOUT, LPMIDIHDR, UINT);
+UINT STDCALL midiOutPrepareHeader(HMIDIOUT, LPMIDIHDR, UINT);
+UINT STDCALL midiOutUnprepareHeader(HMIDIOUT, LPMIDIHDR, UINT);
+UINT STDCALL midiOutReset(HMIDIOUT);
+UINT STDCALL midiOutClose(HMIDIOUT);
+
+UINT STDCALL waveOutOpen(LPHWAVEOUT, UINT, LPWAVEFORMAT, DWORD, DWORD, DWORD);
+UINT STDCALL waveOutWrite(HWAVEOUT, LPWAVEHDR, UINT);
+UINT STDCALL waveOutPrepareHeader(HWAVEOUT, LPWAVEHDR, UINT);
+UINT STDCALL waveOutUnprepareHeader(HWAVEOUT, LPWAVEHDR, UINT);
+UINT STDCALL waveOutReset(HWAVEOUT);
+UINT STDCALL waveOutClose(HWAVEOUT);
+
+UINT STDCALL waveOutGetErrorTextA(UINT, LPSTR, UINT);
+
+BOOL STDCALL sndPlaySoundA(LPCSTR, UINT);
+}
+
+typedef struct {
+ char RIFF[4];
+ Bit32u length;
+ char TYPE[4];
+ char chnk[4];
+ Bit32u chnklen;
+ PCMWAVEFORMAT waveformat;
+ char chnk2[4];
+ Bit32u chnk2len;
+ char data[1];
+} WAVEFILEHEADER, *LPWAVEFILEHEADER;
+#pragma pack(0)
+
+#endif // MMSYSERR_NOERROR defined
+
+class bx_sound_windows_c : public bx_sound_output_c {
+public:
+ bx_sound_windows_c(bx_sb16_c *sb16);
+ BX_SOUND_VIRTUAL ~bx_sound_windows_c();
+
+ // if virtual functions are used, we have to override them
+ // and define our own. Otherwise this file will just implement
+ // the original functions
+#ifdef BX_USE_SOUND_VIRTUAL
+ BX_SOUND_VIRTUAL int waveready();
+ BX_SOUND_VIRTUAL int midiready();
+
+ BX_SOUND_VIRTUAL int openmidioutput(char *device);
+ BX_SOUND_VIRTUAL int sendmidicommand(int delta, int command, int length, Bit8u data[]);
+ BX_SOUND_VIRTUAL int closemidioutput();
+
+ BX_SOUND_VIRTUAL int openwaveoutput(char *device);
+ BX_SOUND_VIRTUAL int startwaveplayback(int frequency, int bits, int stereo, int format);
+ BX_SOUND_VIRTUAL int sendwavepacket(int length, Bit8u data[]);
+ BX_SOUND_VIRTUAL int stopwaveplayback();
+ BX_SOUND_VIRTUAL int closewaveoutput();
+#endif
+
+private:
+ bx_sb16_c *sb16;
+
+ struct bx_sb16_waveinfo_struct {
+ int frequency;
+ int bits;
+ int stereo;
+ int format;
+ };
+
+ HMIDIOUT MidiOut; // Midi output device
+ int MidiOpen; // is it open?
+ HWAVEOUT WaveOut; // Wave output device
+ int WaveOpen; // is it open?
+
+ UINT WaveDevice; // Wave device ID, for waveOutOpen
+
+ // some data for the wave buffers
+ HANDLE DataHandle; // returned by GlobalAlloc()
+ Bit8u *DataPointer; // returned by GlobalLock()
+
+ LPWAVEHDR WaveHeader[BX_SOUND_WINDOWS_NBUF];
+ LPSTR WaveData[BX_SOUND_WINDOWS_NBUF];
+ int length[BX_SOUND_WINDOWS_NBUF]; // length of the data in the buffer
+ int needreopen; // if the format has changed
+ int head,tailfull,tailplay; // These are for three states of the buffers: empty, full, played
+ bx_sb16_waveinfo_struct WaveInfo; // format for the next buffer to be played
+ int iswaveready;
+
+ // and the midi buffer for the SYSEX messages
+ LPMIDIHDR MidiHeader;
+ LPSTR MidiData;
+ int ismidiready;
+
+ int playnextbuffer();
+ void checkmidiready();
+ void checkwaveready();
+};
+
+#endif // defined(WIN32)
diff --git a/tools/ioemu/iodev/state_file.cc b/tools/ioemu/iodev/state_file.cc
new file mode 100644
index 0000000000..f7d0d0feef
--- /dev/null
+++ b/tools/ioemu/iodev/state_file.cc
@@ -0,0 +1,136 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: state_file.cc,v 1.9 2001/12/21 19:33:18 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+// Classes for helping to make checkpoints of the emulator state.
+
+
+
+#include "bochs.h"
+#define LOG_THIS log->
+
+
+
+FILE *state_file::get_handle()
+{
+ BX_INFO(("state_file::get_handle()"));
+ return NULL;
+}
+
+void state_file::write(Bit8u)
+{
+ BX_PANIC(("state_file::write(Bit8u)"));
+}
+
+void state_file::write(Bit16u)
+{
+ BX_PANIC(("state_file::write(Bit16u)"));
+}
+
+void state_file::write(Bit32u)
+{
+ BX_PANIC(("state_file::write(Bit32u)"));
+}
+
+void state_file::write(Bit64u)
+{
+ BX_PANIC(("state_file::write(Bit64u)"));
+}
+
+void state_file::write(const void *, size_t)
+{
+ BX_PANIC(("state_file::write(const void *, size_t)"));
+}
+
+void state_file::read(Bit8u &)
+{
+ BX_PANIC(("state_file::read(uint8 &)"));
+}
+
+void state_file::read(Bit16u &)
+{
+ BX_PANIC(("state_file::read(uint16 &)"));
+}
+
+void state_file::read(Bit32u &)
+{
+ BX_PANIC(("state_file::read(uint32 &)"));
+}
+
+void state_file::read(Bit64u &)
+{
+ BX_PANIC(("state_file::read(uint64 &)"));
+}
+
+void state_file::read(void *, size_t)
+{
+ BX_PANIC(("state_file::read(void *, size_t)"));
+}
+
+void state_file::write_check(const char *)
+{
+ BX_PANIC(("state_file::write_check()"));
+}
+
+void state_file::read_check (const char *)
+{
+ BX_PANIC(("state_file::read_check()"));
+}
+
+void
+state_file::init(void)
+{
+ log = new class logfunctions();
+ log->put("STAT");
+ log->settype(GENLOG);
+}
+
+
+state_file::state_file (const char *name, const char *options)
+{
+ UNUSED(name);
+ UNUSED(options);
+ init();
+ BX_DEBUG(( "Init(const char *, const char *)." ));
+}
+
+state_file::state_file (FILE *f)
+{
+ UNUSED(f);
+ init();
+ BX_INFO(("Init(FILE *)."));
+}
+
+state_file::~state_file()
+{
+ BX_DEBUG(("Exit."));
+ if ( log != NULL )
+ {
+ delete log;
+ log = NULL;
+ }
+}
diff --git a/tools/ioemu/iodev/unmapped.cc b/tools/ioemu/iodev/unmapped.cc
new file mode 100644
index 0000000000..5c7aafedb1
--- /dev/null
+++ b/tools/ioemu/iodev/unmapped.cc
@@ -0,0 +1,305 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: unmapped.cc,v 1.22 2003/08/10 17:19:49 akrisak Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS theUnmappedDevice->
+
+
+bx_unmapped_c *theUnmappedDevice = NULL;
+
+ int
+libunmapped_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theUnmappedDevice = new bx_unmapped_c ();
+ bx_devices.pluginUnmapped = theUnmappedDevice;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theUnmappedDevice, BX_PLUGIN_UNMAPPED);
+ return(0); // Success
+}
+
+ void
+libunmapped_LTX_plugin_fini(void)
+{
+}
+
+bx_unmapped_c::bx_unmapped_c(void)
+{
+ put("UNMP");
+ settype(UNMAPLOG);
+ s.port80 = 0x00;
+ s.port8e = 0x00;
+ s.shutdown = 0;
+}
+
+bx_unmapped_c::~bx_unmapped_c(void)
+{
+ // Nothing yet
+}
+
+ void
+bx_unmapped_c::init(void)
+{
+ DEV_register_default_ioread_handler(this, read_handler, "Unmapped", 7);
+ DEV_register_default_iowrite_handler(this, write_handler, "Unmapped", 7);
+}
+
+ void
+bx_unmapped_c::reset(unsigned type)
+{
+}
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_unmapped_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_UM_SMF
+ bx_unmapped_c *class_ptr = (bx_unmapped_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+ Bit32u
+bx_unmapped_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_UM_SMF
+ UNUSED(io_len);
+
+ Bit32u retval;
+
+ // This function gets called for access to any IO ports which
+ // are not mapped to any device handler. Reads return 0
+
+ if (address >= 0x02e0 && address <= 0x02ef) {
+ retval = 0;
+ goto return_from_read;
+ }
+
+ switch (address) {
+ case 0x80:
+ retval = BX_UM_THIS s.port80;
+ break;
+ case 0x8e:
+ retval = BX_UM_THIS s.port8e;
+ break;
+#if BX_PORT_E9_HACK
+ // Unused port on ISA - this can be used by the emulated code
+ // to detect it is running inside Bochs and that the debugging
+ // features are available (write 0xFF or something on unused
+ // port 0x80, then read from 0xe9, if value is 0xe9, debug
+ // output is available) (see write() for that) -- Andreas and Emmanuel
+ case 0xe9:
+ retval = 0xe9;
+ break;
+#endif
+ case 0x03df:
+ retval = 0xffffffff;
+ BX_DEBUG(("unsupported IO read from port %04x (CGA)", address));
+ break;
+ case 0x023a:
+ case 0x02f8: /* UART */
+ case 0x02f9: /* UART */
+ case 0x02fb: /* UART */
+ case 0x02fc: /* UART */
+ case 0x02fd: /* UART */
+ case 0x02ea:
+ case 0x02eb:
+ case 0x03e8:
+ case 0x03e9:
+ case 0x03ea:
+ case 0x03eb:
+ case 0x03ec:
+ case 0x03ed:
+ case 0x03f8: /* UART */
+ case 0x03f9: /* UART */
+ case 0x03fb: /* UART */
+ case 0x03fc: /* UART */
+ case 0x03fd: /* UART */
+ case 0x17c6:
+ retval = 0xffffffff;
+ BX_DEBUG(("unsupported IO read from port %04x", address));
+ break;
+ default:
+ retval = 0xffffffff;
+ }
+
+ return_from_read:
+ if (bx_dbg.unsupported_io)
+ switch (io_len) {
+ case 1:
+ retval = (Bit8u)retval;
+ BX_DEBUG(("unmapped: 8-bit read from %04x = %02x", address, retval));
+ break;
+ case 2:
+ retval = (Bit16u)retval;
+ BX_DEBUG(("unmapped: 16-bit read from %04x = %04x", address, retval));
+ break;
+ case 4:
+ BX_DEBUG(("unmapped: 32-bit read from %04x = %08x", address, retval));
+ break;
+ default:
+ BX_DEBUG(("unmapped: %d-bit read from %04x = %x", io_len * 8, address, retval));
+ }
+ return retval;
+}
+
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_unmapped_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_UM_SMF
+ bx_unmapped_c *class_ptr = (bx_unmapped_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len);
+}
+
+ void
+bx_unmapped_c::write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_UM_SMF
+ UNUSED(io_len);
+
+
+ // This function gets called for access to any IO ports which
+ // are not mapped to any device handler. Writes to an unmapped
+ // IO port are ignored.
+
+// ???
+
+ if (address >= 0x02e0 && address <= 0x02ef)
+ goto return_from_write;
+
+ switch (address) {
+ case 0x80: // diagnostic test port to display progress of POST
+ //BX_DEBUG(("Diagnostic port 80h: write = %02xh", (unsigned) value));
+ BX_UM_THIS s.port80 = value;
+ break;
+
+ case 0x8e: // ???
+ BX_UM_THIS s.port8e = value;
+ break;
+
+#if BX_PORT_E9_HACK
+ // This port doesn't exist on normal ISA architecture. However,
+ // we define a convention here, to display on the console of the
+ // system running Bochs, anything that is written to it. The
+ // idea is to provide debug output very early when writing
+ // BIOS or OS code for example, without having to bother with
+ // properly setting up a serial port or anything.
+ //
+ // Idea by Andreas Beck (andreas.beck@ggi-project.org)
+
+ case 0xe9:
+ putchar(value);
+ fflush(stdout);
+ break;
+#endif
+
+ case 0xed: // Dummy port used as I/O delay
+ break;
+ case 0xee: // ???
+ break;
+
+ case 0x2f2:
+ case 0x2f3:
+ case 0x2f4:
+ case 0x2f5:
+ case 0x2f6:
+ case 0x2f7:
+ case 0x3e8:
+ case 0x3e9:
+ case 0x3eb:
+ case 0x3ec:
+ case 0x3ed:
+ // BX_DEBUG(("unsupported IO write to port %04x of %02x",
+ // address, value));
+ break;
+
+ case 0x8900: // Shutdown port, could be moved in a PM device
+ // or a host <-> guest communication device
+ switch (value) {
+ case 'S': if (BX_UM_THIS s.shutdown == 0) BX_UM_THIS s.shutdown = 1; break;
+ case 'h': if (BX_UM_THIS s.shutdown == 1) BX_UM_THIS s.shutdown = 2; break;
+ case 'u': if (BX_UM_THIS s.shutdown == 2) BX_UM_THIS s.shutdown = 3; break;
+ case 't': if (BX_UM_THIS s.shutdown == 3) BX_UM_THIS s.shutdown = 4; break;
+ case 'd': if (BX_UM_THIS s.shutdown == 4) BX_UM_THIS s.shutdown = 5; break;
+ case 'o': if (BX_UM_THIS s.shutdown == 5) BX_UM_THIS s.shutdown = 6; break;
+ case 'w': if (BX_UM_THIS s.shutdown == 6) BX_UM_THIS s.shutdown = 7; break;
+ case 'n': if (BX_UM_THIS s.shutdown == 7) BX_UM_THIS s.shutdown = 8; break;
+#if BX_DEBUGGER
+ // Very handy for debugging:
+ // output 'D' to port 8900, and bochs quits to debugger
+ case 'D': bx_debug_break (); break;
+#endif
+ default : BX_UM_THIS s.shutdown = 0; break;
+ }
+ if (BX_UM_THIS s.shutdown == 8) {
+ bx_user_quit = 1;
+ LOG_THIS setonoff(LOGLEV_PANIC, ACT_FATAL);
+ BX_PANIC(("Shutdown port: shutdown requested"));
+ }
+ break;
+
+ case 0xfedc:
+ bx_dbg.debugger = (value > 0);
+ BX_DEBUG(( "DEBUGGER = %u", (unsigned) bx_dbg.debugger));
+ break;
+
+ default:
+ break;
+ }
+ return_from_write:
+ if (bx_dbg.unsupported_io)
+ switch (io_len) {
+ case 1:
+ BX_INFO(("unmapped: 8-bit write to %04x = %02x", address, value));
+ break;
+ case 2:
+ BX_INFO(("unmapped: 16-bit write to %04x = %04x", address, value));
+ break;
+ case 4:
+ BX_INFO(("unmapped: 32-bit write to %04x = %08x", address, value));
+ break;
+ default:
+ BX_INFO(("unmapped: %d-bit write to %04x = %x", io_len * 8, address, value));
+ break;
+ }
+}
diff --git a/tools/ioemu/iodev/unmapped.h b/tools/ioemu/iodev/unmapped.h
new file mode 100644
index 0000000000..c9ef1dc068
--- /dev/null
+++ b/tools/ioemu/iodev/unmapped.h
@@ -0,0 +1,64 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: unmapped.h,v 1.10 2002/10/24 21:07:52 bdenney Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+#if BX_USE_UM_SMF
+# define BX_UM_SMF static
+# define BX_UM_THIS theUnmappedDevice->
+#else
+# define BX_UM_SMF
+# define BX_UM_THIS this->
+#endif
+
+
+
+class bx_unmapped_c : public bx_devmodel_c {
+public:
+ bx_unmapped_c(void);
+ ~bx_unmapped_c(void);
+
+ virtual void init(void);
+ virtual void reset (unsigned type);
+
+private:
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#if !BX_USE_UM_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len);
+#endif
+
+
+ struct {
+ Bit8u port80;
+ Bit8u port8e;
+ Bit8u shutdown;
+ } s; // state information
+
+ };
diff --git a/tools/ioemu/iodev/vga.cc b/tools/ioemu/iodev/vga.cc
new file mode 100644
index 0000000000..894f80bbb6
--- /dev/null
+++ b/tools/ioemu/iodev/vga.cc
@@ -0,0 +1,3116 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: vga.cc,v 1.94.2.1 2004/02/02 22:37:48 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+// Define BX_PLUGGABLE in files that can be compiled into plugins. For
+// platforms that require a special tag on exported symbols, BX_PLUGGABLE
+// is used to know when we are exporting symbols and when we are importing.
+#define BX_PLUGGABLE
+
+#include "bochs.h"
+
+#define LOG_THIS theVga->
+
+/* NOTES:
+ * I take it data rotate is a true rotate with carry of bit 0 to bit 7.
+ * support map mask (3c5 reg 02)
+ */
+
+/* Notes from cb
+ *
+ * It seems that the vga card should support multi bytes IO reads and write
+ * From my tests, inw(port) return port+1 * 256 + port, except for port 0x3c9
+ * (PEL data register, data cycling). More reverse engineering is needed.
+ * This would fix the gentoo bug.
+ */
+
+// (mch)
+#define VGA_TRACE_FEATURE
+
+// Only reference the array if the tile numbers are within the bounds
+// of the array. If out of bounds, do nothing.
+#define SET_TILE_UPDATED(xtile,ytile,value) \
+ do { \
+ if (((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES)) \
+ BX_VGA_THIS s.vga_tile_updated[(xtile)][(ytile)] = value; \
+ } while (0)
+
+// Only reference the array if the tile numbers are within the bounds
+// of the array. If out of bounds, return 0.
+#define GET_TILE_UPDATED(xtile,ytile) \
+ ((((xtile) < BX_NUM_X_TILES) && ((ytile) < BX_NUM_Y_TILES))? \
+ BX_VGA_THIS s.vga_tile_updated[(xtile)][(ytile)] \
+ : 0)
+
+static const Bit8u ccdat[16][4] = {
+ { 0x00, 0x00, 0x00, 0x00 },
+ { 0xff, 0x00, 0x00, 0x00 },
+ { 0x00, 0xff, 0x00, 0x00 },
+ { 0xff, 0xff, 0x00, 0x00 },
+ { 0x00, 0x00, 0xff, 0x00 },
+ { 0xff, 0x00, 0xff, 0x00 },
+ { 0x00, 0xff, 0xff, 0x00 },
+ { 0xff, 0xff, 0xff, 0x00 },
+ { 0x00, 0x00, 0x00, 0xff },
+ { 0xff, 0x00, 0x00, 0xff },
+ { 0x00, 0xff, 0x00, 0xff },
+ { 0xff, 0xff, 0x00, 0xff },
+ { 0x00, 0x00, 0xff, 0xff },
+ { 0xff, 0x00, 0xff, 0xff },
+ { 0x00, 0xff, 0xff, 0xff },
+ { 0xff, 0xff, 0xff, 0xff },
+};
+
+bx_vga_c *theVga = NULL;
+
+unsigned old_iHeight = 0, old_iWidth = 0, old_MSL = 0, old_BPP = 0;
+
+ int
+libvga_LTX_plugin_init(plugin_t *plugin, plugintype_t type, int argc, char *argv[])
+{
+ theVga = new bx_vga_c ();
+ bx_devices.pluginVgaDevice = theVga;
+ BX_REGISTER_DEVICE_DEVMODEL(plugin, type, theVga, BX_PLUGIN_VGA);
+ return(0); // Success
+}
+
+ void
+libvga_LTX_plugin_fini(void)
+{
+}
+
+bx_vga_c::bx_vga_c(void)
+{
+ put("VGA");
+ s.vga_mem_updated = 0;
+ s.x_tilesize = X_TILESIZE;
+ s.y_tilesize = Y_TILESIZE;
+ timer_id = BX_NULL_TIMER_HANDLE;
+}
+
+
+bx_vga_c::~bx_vga_c(void)
+{
+ // nothing for now
+}
+
+
+ void
+bx_vga_c::init(void)
+{
+ unsigned i;
+ unsigned x,y;
+
+ unsigned addr;
+ for (addr=0x03B4; addr<=0x03B5; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3);
+ }
+
+ for (addr=0x03BA; addr<=0x03BA; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3);
+ }
+
+ for (addr=0x03C0; addr<=0x03CF; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3);
+ }
+
+ for (addr=0x03D4; addr<=0x03D5; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3);
+ }
+
+ for (addr=0x03DA; addr<=0x03DA; addr++) {
+ DEV_register_ioread_handler(this, read_handler, addr, "vga video", 1);
+ DEV_register_iowrite_handler(this, write_handler, addr, "vga video", 3);
+ }
+
+
+ BX_VGA_THIS s.misc_output.color_emulation = 1;
+ BX_VGA_THIS s.misc_output.enable_ram = 1;
+ BX_VGA_THIS s.misc_output.clock_select = 0;
+ BX_VGA_THIS s.misc_output.select_high_bank = 0;
+ BX_VGA_THIS s.misc_output.horiz_sync_pol = 1;
+ BX_VGA_THIS s.misc_output.vert_sync_pol = 1;
+
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics = 1;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size = 0;
+
+ BX_VGA_THIS s.line_offset=80;
+ BX_VGA_THIS s.line_compare=1023;
+ BX_VGA_THIS s.vertical_display_end=399;
+
+ for (i=0; i<=0x18; i++)
+ BX_VGA_THIS s.CRTC.reg[i] = 0;
+ BX_VGA_THIS s.CRTC.address = 0;
+
+ BX_VGA_THIS s.attribute_ctrl.flip_flop = 0;
+ BX_VGA_THIS s.attribute_ctrl.address = 0;
+ BX_VGA_THIS s.attribute_ctrl.video_enabled = 1;
+ for (i=0; i<16; i++)
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[i] = 0;
+ BX_VGA_THIS s.attribute_ctrl.overscan_color = 0;
+ BX_VGA_THIS s.attribute_ctrl.color_plane_enable = 0x0f;
+ BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning = 0;
+ BX_VGA_THIS s.attribute_ctrl.color_select = 0;
+
+ for (i=0; i<256; i++) {
+ BX_VGA_THIS s.pel.data[i].red = 0;
+ BX_VGA_THIS s.pel.data[i].green = 0;
+ BX_VGA_THIS s.pel.data[i].blue = 0;
+ }
+ BX_VGA_THIS s.pel.write_data_register = 0;
+ BX_VGA_THIS s.pel.write_data_cycle = 0;
+ BX_VGA_THIS s.pel.read_data_register = 0;
+ BX_VGA_THIS s.pel.read_data_cycle = 0;
+ BX_VGA_THIS s.pel.dac_state = 0x01;
+ BX_VGA_THIS s.pel.mask = 0xff;
+
+ BX_VGA_THIS s.graphics_ctrl.index = 0;
+ BX_VGA_THIS s.graphics_ctrl.set_reset = 0;
+ BX_VGA_THIS s.graphics_ctrl.enable_set_reset = 0;
+ BX_VGA_THIS s.graphics_ctrl.color_compare = 0;
+ BX_VGA_THIS s.graphics_ctrl.data_rotate = 0;
+ BX_VGA_THIS s.graphics_ctrl.raster_op = 0;
+ BX_VGA_THIS s.graphics_ctrl.read_map_select = 0;
+ BX_VGA_THIS s.graphics_ctrl.write_mode = 0;
+ BX_VGA_THIS s.graphics_ctrl.read_mode = 0;
+ BX_VGA_THIS s.graphics_ctrl.odd_even = 0;
+ BX_VGA_THIS s.graphics_ctrl.chain_odd_even = 0;
+ BX_VGA_THIS s.graphics_ctrl.shift_reg = 0;
+ BX_VGA_THIS s.graphics_ctrl.graphics_alpha = 0;
+ BX_VGA_THIS s.graphics_ctrl.memory_mapping = 2; // monochrome text mode
+ BX_VGA_THIS s.graphics_ctrl.color_dont_care = 0;
+ BX_VGA_THIS s.graphics_ctrl.bitmask = 0;
+ for (i=0; i<4; i++) {
+ BX_VGA_THIS s.graphics_ctrl.latch[i] = 0;
+ }
+
+ BX_VGA_THIS s.sequencer.index = 0;
+ BX_VGA_THIS s.sequencer.map_mask = 0;
+ for (i=0; i<4; i++) {
+ BX_VGA_THIS s.sequencer.map_mask_bit[i] = 0;
+ }
+ BX_VGA_THIS s.sequencer.reset1 = 1;
+ BX_VGA_THIS s.sequencer.reset2 = 1;
+ BX_VGA_THIS s.sequencer.reg1 = 0;
+ BX_VGA_THIS s.sequencer.char_map_select = 0;
+ BX_VGA_THIS s.sequencer.extended_mem = 1; // display mem greater than 64K
+ BX_VGA_THIS s.sequencer.odd_even = 1; // use sequential addressing mode
+ BX_VGA_THIS s.sequencer.chain_four = 0; // use map mask & read map select
+
+ memset(BX_VGA_THIS s.vga_memory, 0, sizeof(BX_VGA_THIS s.vga_memory));
+
+ BX_VGA_THIS s.vga_mem_updated = 0;
+ for (y=0; y<480/Y_TILESIZE; y++)
+ for (x=0; x<640/X_TILESIZE; x++)
+ SET_TILE_UPDATED (x, y, 0);
+
+ {
+ /* ??? should redo this to pass X args */
+ char *argv[1] = { "bochs" };
+ bx_gui->init(1, &argv[0], BX_VGA_THIS s.x_tilesize, BX_VGA_THIS s.y_tilesize);
+ }
+
+ BX_INFO(("interval=%u", bx_options.Ovga_update_interval->get ()));
+ if (BX_VGA_THIS timer_id == BX_NULL_TIMER_HANDLE) {
+ BX_VGA_THIS timer_id = bx_pc_system.register_timer(this, timer_handler,
+ bx_options.Ovga_update_interval->get (), 1, 1, "vga");
+ }
+
+ /* video card with BIOS ROM */
+ DEV_cmos_set_reg(0x14, (DEV_cmos_get_reg(0x14) & 0xcf) | 0x00);
+
+ BX_VGA_THIS s.charmap_address = 0;
+ BX_VGA_THIS s.x_dotclockdiv2 = 0;
+ BX_VGA_THIS s.y_doublescan = 0;
+
+#if BX_SUPPORT_VBE
+ // The following is for the vbe display extension
+
+ for (addr=VBE_DISPI_IOPORT_INDEX; addr<=VBE_DISPI_IOPORT_DATA; addr++) {
+ DEV_register_ioread_handler(this, vbe_read_handler, addr, "vga video", 7);
+ DEV_register_iowrite_handler(this, vbe_write_handler, addr, "vga video", 7);
+ }
+#if !BX_PCI_USB_SUPPORT
+ for (addr=VBE_DISPI_IOPORT_INDEX_OLD; addr<=VBE_DISPI_IOPORT_DATA_OLD; addr++) {
+ DEV_register_ioread_handler(this, vbe_read_handler, addr, "vga video", 7);
+ DEV_register_iowrite_handler(this, vbe_write_handler, addr, "vga video", 7);
+ }
+#endif
+ BX_VGA_THIS s.vbe_cur_dispi=VBE_DISPI_ID0;
+ BX_VGA_THIS s.vbe_xres=640;
+ BX_VGA_THIS s.vbe_yres=480;
+ BX_VGA_THIS s.vbe_bpp=8;
+ BX_VGA_THIS s.vbe_bank=0;
+ BX_VGA_THIS s.vbe_enabled=0;
+ BX_VGA_THIS s.vbe_curindex=0;
+ BX_VGA_THIS s.vbe_offset_x=0;
+ BX_VGA_THIS s.vbe_offset_y=0;
+ BX_VGA_THIS s.vbe_virtual_xres=640;
+ BX_VGA_THIS s.vbe_virtual_yres=480;
+ BX_VGA_THIS s.vbe_bpp_multiplier=1;
+ BX_VGA_THIS s.vbe_virtual_start=0;
+ BX_VGA_THIS s.vbe_line_byte_width=640;
+ BX_VGA_THIS s.vbe_lfb_enabled=0;
+
+
+ BX_INFO(("VBE Bochs Display Extension Enabled"));
+#endif
+ bios_init();
+}
+
+ void
+bx_vga_c::bios_init()
+{
+ int i;
+
+ BX_VGA_THIS s.misc_output.color_emulation = 1;
+ BX_VGA_THIS s.misc_output.enable_ram = 1;
+ BX_VGA_THIS s.misc_output.clock_select = 1;
+ BX_VGA_THIS s.misc_output.select_high_bank = 1;
+ BX_VGA_THIS s.misc_output.horiz_sync_pol = 1;
+ BX_VGA_THIS s.misc_output.vert_sync_pol = 0;
+ BX_VGA_THIS s.CRTC.address = 15;
+ BX_VGA_THIS s.CRTC.reg[0] = 95;
+ BX_VGA_THIS s.CRTC.reg[1] = 79;
+ BX_VGA_THIS s.CRTC.reg[2] = 80;
+ BX_VGA_THIS s.CRTC.reg[3] = 130;
+ BX_VGA_THIS s.CRTC.reg[4] = 85;
+ BX_VGA_THIS s.CRTC.reg[5] = 129;
+ BX_VGA_THIS s.CRTC.reg[6] = 191;
+ BX_VGA_THIS s.CRTC.reg[7] = 31;
+ BX_VGA_THIS s.CRTC.reg[8] = 0;
+ BX_VGA_THIS s.CRTC.reg[9] = 79;
+ BX_VGA_THIS s.CRTC.reg[10] = 14;
+ BX_VGA_THIS s.CRTC.reg[11] = 15;
+ BX_VGA_THIS s.CRTC.reg[12] = 0;
+ BX_VGA_THIS s.CRTC.reg[13] = 0;
+ BX_VGA_THIS s.CRTC.reg[14] = 5;
+ BX_VGA_THIS s.CRTC.reg[15] = 160;
+ BX_VGA_THIS s.CRTC.reg[16] = 156;
+ BX_VGA_THIS s.CRTC.reg[17] = 142;
+ BX_VGA_THIS s.CRTC.reg[18] = 143;
+ BX_VGA_THIS s.CRTC.reg[19] = 40;
+ BX_VGA_THIS s.CRTC.reg[20] = 31;
+ BX_VGA_THIS s.CRTC.reg[21] = 150;
+ BX_VGA_THIS s.CRTC.reg[22] = 185;
+ BX_VGA_THIS s.CRTC.reg[23] = 163;
+ BX_VGA_THIS s.CRTC.reg[24] = 255;
+ BX_VGA_THIS s.attribute_ctrl.flip_flop = 1;
+ BX_VGA_THIS s.attribute_ctrl.address = 0;
+ BX_VGA_THIS s.attribute_ctrl.video_enabled = 1;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[0] = 0;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[1] = 1;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[2] = 2;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[3] = 3;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[4] = 4;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[5] = 5;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[6] = 6;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[7] = 7;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[8] = 8;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[9] = 9;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[10] = 10;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[11] = 11;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[12] = 12;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[13] = 13;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[14] = 14;
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[15] = 15;
+ BX_VGA_THIS s.attribute_ctrl.overscan_color = 0;
+ BX_VGA_THIS s.attribute_ctrl.color_plane_enable = 15;
+ BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning = 8;
+ BX_VGA_THIS s.attribute_ctrl.color_select = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics = 1;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity = 1;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select = 0;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size = 0;
+ BX_VGA_THIS s.pel.write_data_register = 16;
+ BX_VGA_THIS s.pel.write_data_cycle = 0;
+ BX_VGA_THIS s.pel.read_data_register = 0;
+ BX_VGA_THIS s.pel.read_data_cycle = 0;
+ BX_VGA_THIS s.pel.dac_state = 0;
+ memset((BX_VGA_THIS s.pel.data), 0, 256);
+ BX_VGA_THIS s.pel.data[0].red = 0;
+ BX_VGA_THIS s.pel.data[0].green = 0;
+ BX_VGA_THIS s.pel.data[0].blue = 0;
+ BX_VGA_THIS s.pel.mask = 255;
+ BX_VGA_THIS s.graphics_ctrl.index = 6;
+ BX_VGA_THIS s.graphics_ctrl.set_reset = 0;
+ BX_VGA_THIS s.graphics_ctrl.enable_set_reset = 0;
+ BX_VGA_THIS s.graphics_ctrl.color_compare = 0;
+ BX_VGA_THIS s.graphics_ctrl.data_rotate = 0;
+ BX_VGA_THIS s.graphics_ctrl.raster_op = 0;
+ BX_VGA_THIS s.graphics_ctrl.read_map_select = 0;
+ BX_VGA_THIS s.graphics_ctrl.write_mode = 0;
+ BX_VGA_THIS s.graphics_ctrl.read_mode = 0;
+ BX_VGA_THIS s.graphics_ctrl.odd_even = 1;
+ BX_VGA_THIS s.graphics_ctrl.chain_odd_even = 1;
+ BX_VGA_THIS s.graphics_ctrl.shift_reg = 0;
+ BX_VGA_THIS s.graphics_ctrl.graphics_alpha = 0;
+ BX_VGA_THIS s.graphics_ctrl.memory_mapping = 3;
+ BX_VGA_THIS s.graphics_ctrl.color_dont_care = 15;
+ BX_VGA_THIS s.graphics_ctrl.bitmask = 255;
+ BX_VGA_THIS s.graphics_ctrl.latch[0] = 0;
+ BX_VGA_THIS s.graphics_ctrl.latch[1] = 0;
+ BX_VGA_THIS s.graphics_ctrl.latch[2] = 0;
+ BX_VGA_THIS s.graphics_ctrl.latch[3] = 0;
+ BX_VGA_THIS s.sequencer.index = 3;
+ BX_VGA_THIS s.sequencer.map_mask = 3;
+ BX_VGA_THIS s.sequencer.map_mask_bit[0] = 1;
+ BX_VGA_THIS s.sequencer.map_mask_bit[1] = 1;
+ BX_VGA_THIS s.sequencer.map_mask_bit[2] = 0;
+ BX_VGA_THIS s.sequencer.map_mask_bit[3] = 0;
+ BX_VGA_THIS s.sequencer.reset1 = 1;
+ BX_VGA_THIS s.sequencer.reset2 = 1;
+ BX_VGA_THIS s.sequencer.reg1 = 0;
+ BX_VGA_THIS s.sequencer.char_map_select = 0;
+ BX_VGA_THIS s.sequencer.extended_mem = 1;
+ BX_VGA_THIS s.sequencer.odd_even = 0;
+ BX_VGA_THIS s.sequencer.chain_four = 0;
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ BX_VGA_THIS s.x_tilesize = 16;
+ BX_VGA_THIS s.y_tilesize = 24;
+ BX_VGA_THIS s.line_offset = 160;
+ BX_VGA_THIS s.line_compare = 1023;
+ BX_VGA_THIS s.vertical_display_end = 399;
+ memset((BX_VGA_THIS s.vga_tile_updated), 0, BX_NUM_X_TILES * BX_NUM_Y_TILES);
+
+ memset((BX_VGA_THIS s.vga_memory), ' ', 256 * 1024);
+ for(i = 0; i < 256 * 1024;i+=2) {
+ BX_VGA_THIS s.vga_memory[i] = ' ';
+ BX_VGA_THIS s.vga_memory[i+1] = 0x07;
+
+ }
+ memset((BX_VGA_THIS s.text_snapshot), 0, 32 * 1024);
+ memset((BX_VGA_THIS s.rgb), 0, 3 * 256);
+ memset((BX_VGA_THIS s.tile), 0, X_TILESIZE * Y_TILESIZE * 4);
+ BX_VGA_THIS s.charmap_address = 0;
+ BX_VGA_THIS s.x_dotclockdiv2 = 0;
+ BX_VGA_THIS s.y_doublescan = 1;
+}
+
+ void
+bx_vga_c::reset(unsigned type)
+{
+}
+
+
+ void
+bx_vga_c::determine_screen_dimensions(unsigned *piHeight, unsigned *piWidth)
+{
+ int ai[0x20];
+ int i,h,v;
+ for ( i = 0 ; i < 0x20 ; i++ )
+ ai[i] = BX_VGA_THIS s.CRTC.reg[i];
+
+ h = (ai[1] + 1) * 8;
+ v = (ai[18] | ((ai[7] & 0x02) << 7) | ((ai[7] & 0x40) << 3)) + 1;
+
+ if ( BX_VGA_THIS s.graphics_ctrl.shift_reg == 0 )
+ {
+ *piWidth = 640;
+ *piHeight = 480;
+
+ if ( BX_VGA_THIS s.CRTC.reg[6] == 0xBF )
+ {
+ if (BX_VGA_THIS s.CRTC.reg[23] == 0xA3 &&
+ BX_VGA_THIS s.CRTC.reg[20] == 0x40 &&
+ BX_VGA_THIS s.CRTC.reg[9] == 0x41)
+ {
+ *piWidth = 320;
+ *piHeight = 240;
+ }
+ else {
+ if (BX_VGA_THIS s.x_dotclockdiv2) h <<= 1;
+ *piWidth = h;
+ *piHeight = v;
+ }
+ }
+ else if ((h >= 640) && (v >= 480)) {
+ *piWidth = h;
+ *piHeight = v;
+ }
+ }
+ else if ( BX_VGA_THIS s.graphics_ctrl.shift_reg == 2 )
+ {
+
+ if ( BX_VGA_THIS s.sequencer.chain_four )
+ {
+ *piWidth = h;
+ *piHeight = v;
+ }
+ else
+ {
+ *piWidth = h;
+ *piHeight = v;
+ }
+ }
+ else
+ {
+ if (BX_VGA_THIS s.x_dotclockdiv2) h <<= 1;
+ *piWidth = h;
+ *piHeight = v;
+ }
+}
+
+
+ // static IO port read callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ Bit32u
+bx_vga_c::read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_VGA_SMF
+ bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
+
+ return( class_ptr->read(address, io_len) );
+}
+
+
+ Bit32u
+bx_vga_c::read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_VGA_SMF
+ bx_bool horiz_retrace = 0, vert_retrace = 0;
+ Bit64u usec;
+ Bit16u vertres;
+ Bit8u retval;
+
+#if defined(VGA_TRACE_FEATURE)
+ Bit32u ret = 0;
+#define RETURN(x) do { ret = (x); goto read_return; } while (0)
+#else
+#define RETURN return
+#endif
+
+#ifdef __OS2__
+ if ( bx_options.videomode == BX_VIDEO_DIRECT )
+ {
+ return _inp(address);
+ }
+#endif
+
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io read from 0x%04x", (unsigned) address));
+#endif
+
+ if ( (address >= 0x03b0) && (address <= 0x03bf) &&
+ (BX_VGA_THIS s.misc_output.color_emulation) ) {
+ RETURN(0xff);
+ }
+ if ( (address >= 0x03d0) && (address <= 0x03df) &&
+ (BX_VGA_THIS s.misc_output.color_emulation==0) ) {
+ RETURN(0xff);
+ }
+
+ switch (address) {
+ case 0x03ba: /* Input Status 1 (monochrome emulation modes) */
+ case 0x03ca: /* Feature Control ??? */
+ case 0x03da: /* Input Status 1 (color emulation modes) */
+ // bit3: Vertical Retrace
+ // 0 = display is in the display mode
+ // 1 = display is in the vertical retrace mode
+ // bit0: Display Enable
+ // 0 = display is in the display mode
+ // 1 = display is not in the display mode; either the
+ // horizontal or vertical retrace period is active
+
+ // using 72 Hz vertical frequency
+ usec = bx_pc_system.time_usec();
+ switch ( ( BX_VGA_THIS s.misc_output.vert_sync_pol << 1) | BX_VGA_THIS s.misc_output.horiz_sync_pol )
+ {
+ case 0: vertres = 200; break;
+ case 1: vertres = 400; break;
+ case 2: vertres = 350; break;
+ default: vertres = 480; break;
+ }
+ if ((usec % 13888) < 70) {
+ vert_retrace = 1;
+ }
+ if ((usec % (13888 / vertres)) == 0) {
+ horiz_retrace = 1;
+ }
+
+ retval = 0;
+ if (horiz_retrace || vert_retrace)
+ retval = 0x01;
+ if (vert_retrace)
+ retval |= 0x08;
+
+ /* reading this port resets the flip-flop to address mode */
+ BX_VGA_THIS s.attribute_ctrl.flip_flop = 0;
+ RETURN(retval);
+ break;
+
+
+ case 0x03c0: /* */
+ if (BX_VGA_THIS s.attribute_ctrl.flip_flop == 0) {
+ //BX_INFO(("io read: 0x3c0: flip_flop = 0"));
+ retval =
+ (BX_VGA_THIS s.attribute_ctrl.video_enabled << 5) |
+ BX_VGA_THIS s.attribute_ctrl.address;
+ RETURN(retval);
+ }
+ else {
+ BX_ERROR(("io read: 0x3c0: flip_flop != 0"));
+ return(0);
+ }
+ break;
+
+ case 0x03c1: /* */
+ switch (BX_VGA_THIS s.attribute_ctrl.address) {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ retval = BX_VGA_THIS s.attribute_ctrl.palette_reg[BX_VGA_THIS s.attribute_ctrl.address];
+ RETURN(retval);
+ break;
+ case 0x10: /* mode control register */
+ retval =
+ (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha << 0) |
+ (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type << 1) |
+ (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics << 2) |
+ (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity << 3) |
+ (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat << 5) |
+ (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select << 6) |
+ (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size << 7);
+ RETURN(retval);
+ break;
+ case 0x11: /* overscan color register */
+ RETURN(BX_VGA_THIS s.attribute_ctrl.overscan_color);
+ break;
+ case 0x12: /* color plane enable */
+ RETURN(BX_VGA_THIS s.attribute_ctrl.color_plane_enable);
+ break;
+ case 0x13: /* horizontal PEL panning register */
+ RETURN(BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning);
+ break;
+ case 0x14: /* color select register */
+ RETURN(BX_VGA_THIS s.attribute_ctrl.color_select);
+ break;
+ default:
+ BX_INFO(("io read: 0x3c1: unknown register 0x%02x",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.address));
+ RETURN(0);
+ }
+ break;
+
+ case 0x03c2: /* Input Status 0 */
+ BX_DEBUG(("io read 0x3c2: input status #0: ignoring"));
+ RETURN(0);
+ break;
+
+ case 0x03c3: /* VGA Enable Register */
+ RETURN(1);
+ break;
+
+ case 0x03c4: /* Sequencer Index Register */
+ RETURN(BX_VGA_THIS s.sequencer.index);
+ break;
+
+ case 0x03c5: /* Sequencer Registers 00..04 */
+ switch (BX_VGA_THIS s.sequencer.index) {
+ case 0: /* sequencer: reset */
+ BX_DEBUG(("io read 0x3c5: sequencer reset"));
+ RETURN(BX_VGA_THIS s.sequencer.reset1 | (BX_VGA_THIS s.sequencer.reset2<<1));
+ break;
+ case 1: /* sequencer: clocking mode */
+ BX_DEBUG(("io read 0x3c5: sequencer clocking mode"));
+ RETURN(BX_VGA_THIS s.sequencer.reg1);
+ break;
+ case 2: /* sequencer: map mask register */
+ RETURN(BX_VGA_THIS s.sequencer.map_mask);
+ break;
+ case 3: /* sequencer: character map select register */
+ RETURN(BX_VGA_THIS s.sequencer.char_map_select);
+ break;
+ case 4: /* sequencer: memory mode register */
+ retval =
+ (BX_VGA_THIS s.sequencer.extended_mem << 1) |
+ (BX_VGA_THIS s.sequencer.odd_even << 2) |
+ (BX_VGA_THIS s.sequencer.chain_four << 3);
+ RETURN(retval);
+ break;
+
+ default:
+ BX_DEBUG(("io read 0x3c5: index %u unhandled",
+ (unsigned) BX_VGA_THIS s.sequencer.index));
+ RETURN(0);
+ }
+ break;
+
+ case 0x03c6: /* PEL mask ??? */
+ RETURN(BX_VGA_THIS s.pel.mask);
+ break;
+
+ case 0x03c7: /* DAC state, read = 11b, write = 00b */
+ RETURN(BX_VGA_THIS s.pel.dac_state);
+ break;
+
+ case 0x03c8: /* PEL address write mode */
+ RETURN(BX_VGA_THIS s.pel.write_data_register);
+ break;
+
+ case 0x03c9: /* PEL Data Register, colors 00..FF */
+ if (BX_VGA_THIS s.pel.dac_state == 0x03) {
+ switch (BX_VGA_THIS s.pel.read_data_cycle) {
+ case 0:
+ retval = BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.read_data_register].red;
+ break;
+ case 1:
+ retval = BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.read_data_register].green;
+ break;
+ case 2:
+ retval = BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.read_data_register].blue;
+ break;
+ default:
+ retval = 0; // keep compiler happy
+ }
+ BX_VGA_THIS s.pel.read_data_cycle++;
+ if (BX_VGA_THIS s.pel.read_data_cycle >= 3) {
+ BX_VGA_THIS s.pel.read_data_cycle = 0;
+ BX_VGA_THIS s.pel.read_data_register++;
+ }
+ }
+ else {
+ retval = 0x3f;
+ }
+ RETURN(retval);
+ break;
+
+ case 0x03cc: /* Miscellaneous Output / Graphics 1 Position ??? */
+ retval =
+ ((BX_VGA_THIS s.misc_output.color_emulation & 0x01) << 0) |
+ ((BX_VGA_THIS s.misc_output.enable_ram & 0x01) << 1) |
+ ((BX_VGA_THIS s.misc_output.clock_select & 0x03) << 2) |
+ ((BX_VGA_THIS s.misc_output.select_high_bank & 0x01) << 5) |
+ ((BX_VGA_THIS s.misc_output.horiz_sync_pol & 0x01) << 6) |
+ ((BX_VGA_THIS s.misc_output.vert_sync_pol & 0x01) << 7);
+ RETURN(retval);
+ break;
+
+ case 0x03ce: /* Graphics Controller Index Register */
+ RETURN(BX_VGA_THIS s.graphics_ctrl.index);
+ break;
+
+ case 0x03cd: /* ??? */
+ BX_DEBUG(("io read from 03cd"));
+ RETURN(0x00);
+ break;
+
+ case 0x03cf: /* Graphics Controller Registers 00..08 */
+ switch (BX_VGA_THIS s.graphics_ctrl.index) {
+ case 0: /* Set/Reset */
+ RETURN(BX_VGA_THIS s.graphics_ctrl.set_reset);
+ break;
+ case 1: /* Enable Set/Reset */
+ RETURN(BX_VGA_THIS s.graphics_ctrl.enable_set_reset);
+ break;
+ case 2: /* Color Compare */
+ RETURN(BX_VGA_THIS s.graphics_ctrl.color_compare);
+ break;
+ case 3: /* Data Rotate */
+ retval =
+ ((BX_VGA_THIS s.graphics_ctrl.raster_op & 0x03) << 3) |
+ ((BX_VGA_THIS s.graphics_ctrl.data_rotate & 0x07) << 0);
+ RETURN(retval);
+ break;
+ case 4: /* Read Map Select */
+ RETURN(BX_VGA_THIS s.graphics_ctrl.read_map_select);
+ break;
+ case 5: /* Mode */
+ retval =
+ ((BX_VGA_THIS s.graphics_ctrl.shift_reg & 0x03) << 5) |
+ ((BX_VGA_THIS s.graphics_ctrl.odd_even & 0x01 ) << 4) |
+ ((BX_VGA_THIS s.graphics_ctrl.read_mode & 0x01) << 3) |
+ ((BX_VGA_THIS s.graphics_ctrl.write_mode & 0x03) << 0);
+
+ if (BX_VGA_THIS s.graphics_ctrl.odd_even ||
+ BX_VGA_THIS s.graphics_ctrl.shift_reg)
+ BX_DEBUG(("io read 0x3cf: reg 05 = 0x%02x", (unsigned) retval));
+ RETURN(retval);
+ break;
+ case 6: /* Miscellaneous */
+ retval =
+ ((BX_VGA_THIS s.graphics_ctrl.memory_mapping & 0x03 ) << 2) |
+ ((BX_VGA_THIS s.graphics_ctrl.odd_even & 0x01) << 1) |
+ ((BX_VGA_THIS s.graphics_ctrl.graphics_alpha & 0x01) << 0);
+ RETURN(retval);
+ break;
+ case 7: /* Color Don't Care */
+ RETURN(BX_VGA_THIS s.graphics_ctrl.color_dont_care);
+ break;
+ case 8: /* Bit Mask */
+ RETURN(BX_VGA_THIS s.graphics_ctrl.bitmask);
+ break;
+ default:
+ /* ??? */
+ BX_DEBUG(("io read: 0x3cf: index %u unhandled",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.index));
+ RETURN(0);
+ }
+ break;
+
+ case 0x03d4: /* CRTC Index Register (color emulation modes) */
+ RETURN(BX_VGA_THIS s.CRTC.address);
+ break;
+
+ case 0x03b5: /* CRTC Registers (monochrome emulation modes) */
+ case 0x03d5: /* CRTC Registers (color emulation modes) */
+ if (BX_VGA_THIS s.CRTC.address > 0x18) {
+ BX_DEBUG(("io read: invalid CRTC register 0x%02x",
+ (unsigned) BX_VGA_THIS s.CRTC.address));
+ RETURN(0);
+ }
+ RETURN(BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address]);
+ break;
+
+ case 0x03b4: /* CRTC Index Register (monochrome emulation modes) */
+ case 0x03cb: /* not sure but OpenBSD reads it a lot */
+ default:
+ BX_INFO(("io read from vga port 0x%02x", (unsigned) address));
+ RETURN(0); /* keep compiler happy */
+ }
+
+#if defined(VGA_TRACE_FEATURE)
+ read_return:
+ BX_DEBUG(("8-bit read from %04x = %02x", (unsigned) address, ret));
+ return ret;
+#endif
+}
+#if defined(VGA_TRACE_FEATURE)
+#undef RETURN
+#endif
+
+ // static IO port write callback handler
+ // redirects to non-static class handler to avoid virtual functions
+
+ void
+bx_vga_c::write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_VGA_SMF
+ bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len, 0);
+#else
+ UNUSED(this_ptr);
+ theVga->write(address, value, io_len, 0);
+#endif
+}
+
+ void
+bx_vga_c::write_handler_no_log(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_VGA_SMF
+ bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
+
+ class_ptr->write(address, value, io_len, 1);
+#else
+ UNUSED(this_ptr);
+ theVga->write(address, value, io_len, 1);
+#endif
+}
+
+ void
+bx_vga_c::write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log)
+{
+ unsigned i;
+ Bit8u charmap1, charmap2, prev_memory_mapping;
+ bx_bool prev_video_enabled, prev_line_graphics, prev_int_pal_size;
+ bx_bool prev_graphics_alpha, prev_chain_odd_even;
+ bx_bool needs_update = 0;
+
+#if defined(VGA_TRACE_FEATURE)
+ if (!no_log)
+ switch (io_len) {
+ case 1:
+ BX_DEBUG(("8-bit write to %04x = %02x", (unsigned)address, (unsigned)value));
+ break;
+ case 2:
+ BX_DEBUG(("16-bit write to %04x = %04x", (unsigned)address, (unsigned)value));
+ break;
+ default:
+ BX_PANIC(("Weird VGA write size"));
+ }
+#else
+ if (io_len == 1) {
+ BX_DEBUG(("io write to 0x%04x = 0x%02x", (unsigned) address,
+ (unsigned) value));
+ }
+#endif
+
+ if (io_len == 2) {
+#if BX_USE_VGA_SMF
+ bx_vga_c::write_handler_no_log(0, address, value & 0xff, 1);
+ bx_vga_c::write_handler_no_log(0, address+1, (value >> 8) & 0xff, 1);
+#else
+ bx_vga_c::write(address, value & 0xff, 1, 1);
+ bx_vga_c::write(address+1, (value >> 8) & 0xff, 1, 1);
+#endif
+ return;
+ }
+
+#ifdef __OS2__
+ if ( bx_options.videomode == BX_VIDEO_DIRECT )
+ {
+ _outp(address,value);
+ return;
+ }
+#endif
+
+ if ( (address >= 0x03b0) && (address <= 0x03bf) &&
+ (BX_VGA_THIS s.misc_output.color_emulation) )
+ return;
+ if ( (address >= 0x03d0) && (address <= 0x03df) &&
+ (BX_VGA_THIS s.misc_output.color_emulation==0) )
+ return;
+
+ switch (address) {
+ case 0x03ba: /* Feature Control (monochrome emulation modes) */
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3ba: feature control: ignoring"));
+#endif
+ break;
+
+ case 0x03c0: /* Attribute Controller */
+ if (BX_VGA_THIS s.attribute_ctrl.flip_flop == 0) { /* address mode */
+ prev_video_enabled = BX_VGA_THIS s.attribute_ctrl.video_enabled;
+ BX_VGA_THIS s.attribute_ctrl.video_enabled = (value >> 5) & 0x01;
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c0: video_enabled = %u",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.video_enabled));
+#endif
+ if (BX_VGA_THIS s.attribute_ctrl.video_enabled == 0)
+ bx_gui->clear_screen();
+ else if (!prev_video_enabled) {
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("found enable transition"));
+#endif
+ needs_update = 1;
+ }
+ value &= 0x1f; /* address = bits 0..4 */
+ BX_VGA_THIS s.attribute_ctrl.address = value;
+ switch (value) {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ break;
+
+ default:
+ BX_DEBUG(("io write 3c0: address mode reg=%u",
+ (unsigned) value));
+ }
+ }
+ else { /* data-write mode */
+ switch (BX_VGA_THIS s.attribute_ctrl.address) {
+ case 0x00: case 0x01: case 0x02: case 0x03:
+ case 0x04: case 0x05: case 0x06: case 0x07:
+ case 0x08: case 0x09: case 0x0a: case 0x0b:
+ case 0x0c: case 0x0d: case 0x0e: case 0x0f:
+ if (value != BX_VGA_THIS s.attribute_ctrl.palette_reg[BX_VGA_THIS s.attribute_ctrl.address]) {
+ BX_VGA_THIS s.attribute_ctrl.palette_reg[BX_VGA_THIS s.attribute_ctrl.address] =
+ value;
+ needs_update = 1;
+ }
+ break;
+ case 0x10: // mode control register
+ prev_line_graphics = BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics;
+ prev_int_pal_size = BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha =
+ (value >> 0) & 0x01;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type =
+ (value >> 1) & 0x01;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics =
+ (value >> 2) & 0x01;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity =
+ (value >> 3) & 0x01;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_panning_compat =
+ (value >> 5) & 0x01;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select =
+ (value >> 6) & 0x01;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size =
+ (value >> 7) & 0x01;
+ if (((value >> 2) & 0x01) != prev_line_graphics) {
+ bx_gui->set_text_charmap(
+ & BX_VGA_THIS s.vga_memory[0x20000 + BX_VGA_THIS s.charmap_address]);
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ }
+ if (((value >> 7) & 0x01) != prev_int_pal_size) {
+ needs_update = 1;
+ }
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c0: mode control: %02x h",
+ (unsigned) value));
+#endif
+ break;
+ case 0x11: // Overscan Color Register
+ BX_VGA_THIS s.attribute_ctrl.overscan_color = (value & 0x3f);
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c0: overscan color = %02x",
+ (unsigned) value));
+#endif
+ break;
+ case 0x12: // Color Plane Enable Register
+ BX_VGA_THIS s.attribute_ctrl.color_plane_enable = (value & 0x0f);
+ needs_update = 1;
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c0: color plane enable = %02x",
+ (unsigned) value));
+#endif
+ break;
+ case 0x13: // Horizontal Pixel Panning Register
+ BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning = (value & 0x0f);
+ needs_update = 1;
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c0: horiz pel panning = %02x",
+ (unsigned) value));
+#endif
+ break;
+ case 0x14: // Color Select Register
+ BX_VGA_THIS s.attribute_ctrl.color_select = (value & 0x0f);
+ needs_update = 1;
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c0: color select = %02x",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.color_select));
+#endif
+ break;
+ default:
+ BX_DEBUG(("io write 3c0: data-write mode %02x h",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.address));
+ }
+ }
+ BX_VGA_THIS s.attribute_ctrl.flip_flop = !BX_VGA_THIS s.attribute_ctrl.flip_flop;
+ break;
+
+ case 0x03c2: // Miscellaneous Output Register
+ BX_VGA_THIS s.misc_output.color_emulation = (value >> 0) & 0x01;
+ BX_VGA_THIS s.misc_output.enable_ram = (value >> 1) & 0x01;
+ BX_VGA_THIS s.misc_output.clock_select = (value >> 2) & 0x03;
+ BX_VGA_THIS s.misc_output.select_high_bank = (value >> 5) & 0x01;
+ BX_VGA_THIS s.misc_output.horiz_sync_pol = (value >> 6) & 0x01;
+ BX_VGA_THIS s.misc_output.vert_sync_pol = (value >> 7) & 0x01;
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c2:"));
+ BX_DEBUG((" color_emulation (attempted) = %u",
+ (value >> 0) & 0x01));
+ BX_DEBUG((" enable_ram = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.enable_ram));
+ BX_DEBUG((" clock_select = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.clock_select));
+ BX_DEBUG((" select_high_bank = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.select_high_bank));
+ BX_DEBUG((" horiz_sync_pol = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.horiz_sync_pol));
+ BX_DEBUG((" vert_sync_pol = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.vert_sync_pol));
+#endif
+ break;
+
+ case 0x03c3: // VGA enable
+ // bit0: enables VGA display if set
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c3: (ignoring) VGA enable = %u",
+ (unsigned) (value & 0x01) ));
+#endif
+ break;
+
+ case 0x03c4: /* Sequencer Index Register */
+ if (value > 4) {
+ BX_DEBUG(("io write 3c4: value > 4"));
+ }
+ BX_VGA_THIS s.sequencer.index = value;
+ break;
+
+ case 0x03c5: /* Sequencer Registers 00..04 */
+ switch (BX_VGA_THIS s.sequencer.index) {
+ case 0: /* sequencer: reset */
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("write 0x3c5: sequencer reset: value=0x%02x",
+ (unsigned) value));
+#endif
+ if (BX_VGA_THIS s.sequencer.reset1 && ((value & 0x01) == 0)) {
+ BX_VGA_THIS s.sequencer.char_map_select = 0;
+ BX_VGA_THIS s.charmap_address = 0;
+ bx_gui->set_text_charmap(
+ & BX_VGA_THIS s.vga_memory[0x20000 + BX_VGA_THIS s.charmap_address]);
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ }
+ BX_VGA_THIS s.sequencer.reset1 = (value >> 0) & 0x01;
+ BX_VGA_THIS s.sequencer.reset2 = (value >> 1) & 0x01;
+ break;
+ case 1: /* sequencer: clocking mode */
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c5=%02x: clocking mode reg: ignoring",
+ (unsigned) value));
+#endif
+ BX_VGA_THIS s.sequencer.reg1 = value & 0x3f;
+ BX_VGA_THIS s.x_dotclockdiv2 = ((value & 0x08) > 0);
+ break;
+ case 2: /* sequencer: map mask register */
+ BX_VGA_THIS s.sequencer.map_mask = (value & 0x0f);
+ for (i=0; i<4; i++)
+ BX_VGA_THIS s.sequencer.map_mask_bit[i] = (value >> i) & 0x01;
+ break;
+ case 3: /* sequencer: character map select register */
+ BX_VGA_THIS s.sequencer.char_map_select = value;
+ charmap1 = value & 0x13;
+ if (charmap1 > 3) charmap1 = (charmap1 & 3) + 4;
+ charmap2 = (value & 0x2C) >> 2;
+ if (charmap2 > 3) charmap2 = (charmap2 & 3) + 4;
+ if (BX_VGA_THIS s.CRTC.reg[0x09] > 0) {
+ BX_VGA_THIS s.charmap_address = (charmap1 << 13);
+ bx_gui->set_text_charmap(
+ & BX_VGA_THIS s.vga_memory[0x20000 + BX_VGA_THIS s.charmap_address]);
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ }
+ if (charmap2 != charmap1)
+ BX_INFO(("char map select: #2=%d (unused)", charmap2));
+ break;
+ case 4: /* sequencer: memory mode register */
+ BX_VGA_THIS s.sequencer.extended_mem = (value >> 1) & 0x01;
+ BX_VGA_THIS s.sequencer.odd_even = (value >> 2) & 0x01;
+ BX_VGA_THIS s.sequencer.chain_four = (value >> 3) & 0x01;
+
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write 3c5: index 4:"));
+ BX_DEBUG((" extended_mem %u",
+ (unsigned) BX_VGA_THIS s.sequencer.extended_mem));
+ BX_DEBUG((" odd_even %u",
+ (unsigned) BX_VGA_THIS s.sequencer.odd_even));
+ BX_DEBUG((" chain_four %u",
+ (unsigned) BX_VGA_THIS s.sequencer.chain_four));
+#endif
+ break;
+ default:
+ BX_DEBUG(("io write 3c5: index %u unhandled",
+ (unsigned) BX_VGA_THIS s.sequencer.index));
+ }
+ break;
+
+ case 0x03c6: /* PEL mask */
+ BX_VGA_THIS s.pel.mask = value;
+ if (BX_VGA_THIS s.pel.mask != 0xff)
+ BX_DEBUG(("io write 3c6: PEL mask=0x%02x != 0xFF", value));
+ // BX_VGA_THIS s.pel.mask should be and'd with final value before
+ // indexing into color register BX_VGA_THIS s.pel.data[]
+ break;
+
+ case 0x03c7: // PEL address, read mode
+ BX_VGA_THIS s.pel.read_data_register = value;
+ BX_VGA_THIS s.pel.read_data_cycle = 0;
+ BX_VGA_THIS s.pel.dac_state = 0x03;
+ break;
+
+ case 0x03c8: /* PEL address write mode */
+ BX_VGA_THIS s.pel.write_data_register = value;
+ BX_VGA_THIS s.pel.write_data_cycle = 0;
+ BX_VGA_THIS s.pel.dac_state = 0x00;
+ break;
+
+ case 0x03c9: /* PEL Data Register, colors 00..FF */
+ switch (BX_VGA_THIS s.pel.write_data_cycle) {
+ case 0:
+ BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].red = value;
+ break;
+ case 1:
+ BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].green = value;
+ break;
+ case 2:
+ BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].blue = value;
+
+ needs_update |= bx_gui->palette_change(BX_VGA_THIS s.pel.write_data_register,
+ BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].red<<2,
+ BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].green<<2,
+ BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].blue<<2);
+ break;
+ }
+
+ BX_VGA_THIS s.pel.write_data_cycle++;
+ if (BX_VGA_THIS s.pel.write_data_cycle >= 3) {
+ //BX_INFO(("BX_VGA_THIS s.pel.data[%u] {r=%u, g=%u, b=%u}",
+ // (unsigned) BX_VGA_THIS s.pel.write_data_register,
+ // (unsigned) BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].red,
+ // (unsigned) BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].green,
+ // (unsigned) BX_VGA_THIS s.pel.data[BX_VGA_THIS s.pel.write_data_register].blue);
+ BX_VGA_THIS s.pel.write_data_cycle = 0;
+ BX_VGA_THIS s.pel.write_data_register++;
+ }
+ break;
+
+ case 0x03ca: /* Graphics 2 Position (EGA) */
+ // ignore, EGA only???
+ break;
+
+ case 0x03cc: /* Graphics 1 Position (EGA) */
+ // ignore, EGA only???
+ break;
+
+ case 0x03ce: /* Graphics Controller Index Register */
+ if (value > 0x08) /* ??? */
+ BX_DEBUG(("io write: 3ce: value > 8"));
+ BX_VGA_THIS s.graphics_ctrl.index = value;
+ break;
+
+ case 0x03cd: /* ??? */
+ BX_DEBUG(("io write to 03cd = %02x", (unsigned) value));
+ break;
+
+ case 0x03cf: /* Graphics Controller Registers 00..08 */
+ switch (BX_VGA_THIS s.graphics_ctrl.index) {
+ case 0: /* Set/Reset */
+ BX_VGA_THIS s.graphics_ctrl.set_reset = value & 0x0f;
+ break;
+ case 1: /* Enable Set/Reset */
+ BX_VGA_THIS s.graphics_ctrl.enable_set_reset = value & 0x0f;
+ break;
+ case 2: /* Color Compare */
+ BX_VGA_THIS s.graphics_ctrl.color_compare = value & 0x0f;
+ break;
+ case 3: /* Data Rotate */
+ BX_VGA_THIS s.graphics_ctrl.data_rotate = value & 0x07;
+ /* ??? is this bits 3..4 or 4..5 */
+ BX_VGA_THIS s.graphics_ctrl.raster_op = (value >> 3) & 0x03; /* ??? */
+ break;
+ case 4: /* Read Map Select */
+ BX_VGA_THIS s.graphics_ctrl.read_map_select = value & 0x03;
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("io write to 03cf = %02x (RMS)", (unsigned) value));
+#endif
+ break;
+ case 5: /* Mode */
+ BX_VGA_THIS s.graphics_ctrl.write_mode = value & 0x03;
+ BX_VGA_THIS s.graphics_ctrl.read_mode = (value >> 3) & 0x01;
+ BX_VGA_THIS s.graphics_ctrl.odd_even = (value >> 4) & 0x01;
+ BX_VGA_THIS s.graphics_ctrl.shift_reg = (value >> 5) & 0x03;
+
+ if (BX_VGA_THIS s.graphics_ctrl.odd_even)
+ BX_DEBUG(("io write: 3cf: reg 05: value = %02xh",
+ (unsigned) value));
+ if (BX_VGA_THIS s.graphics_ctrl.shift_reg)
+ BX_DEBUG(("io write: 3cf: reg 05: value = %02xh",
+ (unsigned) value));
+ break;
+ case 6: /* Miscellaneous */
+ prev_graphics_alpha = BX_VGA_THIS s.graphics_ctrl.graphics_alpha;
+ prev_chain_odd_even = BX_VGA_THIS s.graphics_ctrl.chain_odd_even;
+ prev_memory_mapping = BX_VGA_THIS s.graphics_ctrl.memory_mapping;
+
+ BX_VGA_THIS s.graphics_ctrl.graphics_alpha = value & 0x01;
+ BX_VGA_THIS s.graphics_ctrl.chain_odd_even = (value >> 1) & 0x01;
+ BX_VGA_THIS s.graphics_ctrl.memory_mapping = (value >> 2) & 0x03;
+#if !defined(VGA_TRACE_FEATURE)
+ BX_DEBUG(("memory_mapping set to %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping));
+ BX_DEBUG(("graphics mode set to %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.graphics_alpha));
+ BX_DEBUG(("odd_even mode set to %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.odd_even));
+ BX_DEBUG(("io write: 3cf: reg 06: value = %02xh",
+ (unsigned) value));
+#endif
+ if (prev_memory_mapping != BX_VGA_THIS s.graphics_ctrl.memory_mapping)
+ needs_update = 1;
+ if (prev_graphics_alpha != BX_VGA_THIS s.graphics_ctrl.graphics_alpha) {
+ needs_update = 1;
+ old_iHeight = 0;
+ }
+ break;
+ case 7: /* Color Don't Care */
+ BX_VGA_THIS s.graphics_ctrl.color_dont_care = value & 0x0f;
+ break;
+ case 8: /* Bit Mask */
+ BX_VGA_THIS s.graphics_ctrl.bitmask = value;
+ break;
+ default:
+ /* ??? */
+ BX_DEBUG(("io write: 3cf: index %u unhandled",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.index));
+ }
+ break;
+
+ case 0x03b4: /* CRTC Index Register (monochrome emulation modes) */
+ case 0x03d4: /* CRTC Index Register (color emulation modes) */
+ BX_VGA_THIS s.CRTC.address = value & 0x7f;
+ if (BX_VGA_THIS s.CRTC.address > 0x18)
+ BX_DEBUG(("write: invalid CRTC register 0x%02x selected",
+ (unsigned) BX_VGA_THIS s.CRTC.address));
+ break;
+
+ case 0x03b5: /* CRTC Registers (monochrome emulation modes) */
+ case 0x03d5: /* CRTC Registers (color emulation modes) */
+ if (BX_VGA_THIS s.CRTC.address > 0x18) {
+ BX_DEBUG(("write: invalid CRTC register 0x%02x ignored",
+ (unsigned) BX_VGA_THIS s.CRTC.address));
+ return;
+ }
+ if (value != BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address]) {
+ BX_VGA_THIS s.CRTC.reg[BX_VGA_THIS s.CRTC.address] = value;
+ switch (BX_VGA_THIS s.CRTC.address) {
+ case 0x07:
+ BX_VGA_THIS s.vertical_display_end &= 0xff;
+ if (BX_VGA_THIS s.CRTC.reg[0x07] & 0x02) BX_VGA_THIS s.vertical_display_end |= 0x100;
+ if (BX_VGA_THIS s.CRTC.reg[0x07] & 0x40) BX_VGA_THIS s.vertical_display_end |= 0x200;
+ BX_VGA_THIS s.line_compare &= 0x2ff;
+ if (BX_VGA_THIS s.CRTC.reg[0x07] & 0x10) BX_VGA_THIS s.line_compare |= 0x100;
+ needs_update = 1;
+ break;
+ case 0x08:
+ // Vertical pel panning change
+ needs_update = 1;
+ break;
+ case 0x09:
+ BX_VGA_THIS s.y_doublescan = ((value & 0x9f) > 0);
+ BX_VGA_THIS s.line_compare &= 0x1ff;
+ if (BX_VGA_THIS s.CRTC.reg[0x09] & 0x40) BX_VGA_THIS s.line_compare |= 0x200;
+ needs_update = 1;
+ break;
+ case 0x0A:
+ case 0x0B:
+ case 0x0E:
+ case 0x0F:
+ // Cursor size / location change
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ break;
+ case 0x0C:
+ case 0x0D:
+ // Start address change
+ if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) {
+ needs_update = 1;
+ } else {
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ }
+ break;
+ case 0x12:
+ BX_VGA_THIS s.vertical_display_end &= 0x300;
+ BX_VGA_THIS s.vertical_display_end |= BX_VGA_THIS s.CRTC.reg[0x12];
+ break;
+ case 0x13:
+ case 0x14:
+ case 0x17:
+ // Line offset change
+ BX_VGA_THIS s.line_offset = BX_VGA_THIS s.CRTC.reg[0x13] << 1;
+ if (BX_VGA_THIS s.CRTC.reg[0x14] & 0x40) BX_VGA_THIS s.line_offset <<= 2;
+ else if ((BX_VGA_THIS s.CRTC.reg[0x17] & 0x40) == 0) BX_VGA_THIS s.line_offset <<= 1;
+ needs_update = 1;
+ break;
+ case 0x18:
+ BX_VGA_THIS s.line_compare &= 0x300;
+ BX_VGA_THIS s.line_compare |= BX_VGA_THIS s.CRTC.reg[0x18];
+ needs_update = 1;
+ break;
+ }
+
+ }
+ break;
+
+ case 0x03da: /* Feature Control (color emulation modes) */
+ BX_DEBUG(("io write: 3da: ignoring: feature ctrl & vert sync"));
+ break;
+
+ case 0x03c1: /* */
+ default:
+ BX_ERROR(("unsupported io write to port 0x%04x, val=0x%02x",
+ (unsigned) address, (unsigned) value));
+ }
+ if (needs_update) {
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ // Mark all video as updated so the changes will go through
+ if ((BX_VGA_THIS s.graphics_ctrl.graphics_alpha)
+#if BX_SUPPORT_VBE
+ || (BX_VGA_THIS s.vbe_enabled)
+#endif
+ ) {
+ for (unsigned xti = 0; xti < BX_NUM_X_TILES; xti++) {
+ for (unsigned yti = 0; yti < BX_NUM_Y_TILES; yti++) {
+ SET_TILE_UPDATED (xti, yti, 1);
+ }
+ }
+ } else {
+ memset(BX_VGA_THIS s.text_snapshot, 0,
+ sizeof(BX_VGA_THIS s.text_snapshot));
+ }
+ }
+}
+
+void
+bx_vga_c::set_update_interval (unsigned interval)
+{
+ BX_INFO (("Changing timer interval to %d\n", interval));
+ BX_VGA_THIS timer_handler (theVga);
+ bx_pc_system.activate_timer (BX_VGA_THIS timer_id, interval, 1);
+}
+
+ void
+bx_vga_c::trigger_timer(void *this_ptr)
+{
+ timer_handler(this_ptr);
+}
+
+ void
+bx_vga_c::timer_handler(void *this_ptr)
+{
+#if !BX_USE_VGA_SMF
+
+ bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
+
+ class_ptr->timer();
+}
+
+ void
+bx_vga_c::timer(void)
+{
+#else
+ UNUSED(this_ptr);
+#endif
+
+ update();
+ bx_gui->flush();
+
+}
+
+
+ void
+bx_vga_c::update(void)
+{
+ unsigned iHeight, iWidth;
+
+ /* no screen update necessary */
+ if (BX_VGA_THIS s.vga_mem_updated==0)
+ return;
+
+ /* skip screen update when the sequencer is in reset mode or video is disabled */
+ if (!BX_VGA_THIS s.sequencer.reset1 || !BX_VGA_THIS s.sequencer.reset2
+ || !BX_VGA_THIS s.attribute_ctrl.video_enabled)
+ return;
+
+ /* skip screen update if the vertical retrace is in progress
+ (using 72 Hz vertical frequency) */
+ if ((bx_pc_system.time_usec() % 13888) < 70)
+ return;
+
+#if BX_SUPPORT_VBE
+ if ((BX_VGA_THIS s.vbe_enabled) && (BX_VGA_THIS s.vbe_bpp != VBE_DISPI_BPP_4))
+ {
+ // specific VBE code display update code
+ // this is partly copied/modified from the 320x200x8 update more below
+ unsigned xc, yc, xti, yti;
+ unsigned r;
+ unsigned long pixely, bmp_ofs_y, tile_ofs_y;
+
+ if (BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_32)
+ {
+ Bit32u *vidmem = (Bit32u *)(&BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start]);
+ Bit32u *tile = (Bit32u *)(BX_VGA_THIS s.tile);
+ Bit16u width = BX_VGA_THIS s.vbe_virtual_xres;
+
+ Bit32u *vidptr, *tileptr;
+
+ iWidth=BX_VGA_THIS s.vbe_xres;
+ iHeight=BX_VGA_THIS s.vbe_yres;
+
+ for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++)
+ {
+ for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++)
+ {
+ if (GET_TILE_UPDATED (xti, yti))
+ {
+ for (r=0; r<Y_TILESIZE; r++)
+ {
+ pixely = yc + r;
+ // calc offsets into video and tile memory
+ bmp_ofs_y = pixely*width;
+ tile_ofs_y = r*X_TILESIZE;
+ // get offsets so that we do less calc in the inner loop
+ vidptr = &vidmem[bmp_ofs_y+xc];
+ tileptr = &tile[tile_ofs_y];
+ memmove(tileptr, vidptr, X_TILESIZE<<2);
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ }
+ else if (BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_24)
+ {
+ Bit8u *vidmem = &BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start];
+ Bit8u *tile = BX_VGA_THIS s.tile;
+ Bit16u width = BX_VGA_THIS s.vbe_virtual_xres*3;
+
+ Bit8u *vidptr, *tileptr;
+
+ iWidth=BX_VGA_THIS s.vbe_xres;
+ iHeight=BX_VGA_THIS s.vbe_yres;
+
+ for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++)
+ {
+ for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++)
+ {
+ if (GET_TILE_UPDATED (xti, yti))
+ {
+ for (r=0; r<Y_TILESIZE; r++)
+ {
+ pixely = yc + r;
+ // calc offsets into video and tile memory
+ bmp_ofs_y = pixely*width;
+ tile_ofs_y = r*X_TILESIZE*3;
+ // get offsets so that we do less calc in the inner loop
+ vidptr = &vidmem[bmp_ofs_y+xc*3];
+ tileptr = &tile[tile_ofs_y];
+ memmove(tileptr, vidptr, X_TILESIZE*3);
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ }
+ else if ((BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_15) ||
+ (BX_VGA_THIS s.vbe_bpp == VBE_DISPI_BPP_16))
+ {
+ Bit16u *vidmem = (Bit16u *)(&BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start]);
+ Bit16u *tile = (Bit16u *)(BX_VGA_THIS s.tile);
+ Bit16u width = BX_VGA_THIS s.vbe_virtual_xres;
+
+ Bit16u *vidptr, *tileptr;
+
+ iWidth=BX_VGA_THIS s.vbe_xres;
+ iHeight=BX_VGA_THIS s.vbe_yres;
+
+ for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++)
+ {
+ for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++)
+ {
+ if (GET_TILE_UPDATED (xti, yti))
+ {
+ for (r=0; r<Y_TILESIZE; r++)
+ {
+ pixely = yc + r;
+ // calc offsets into video and tile memory
+ bmp_ofs_y = pixely*width;
+ tile_ofs_y = r*X_TILESIZE;
+ // get offsets so that we do less calc in the inner loop
+ vidptr = &vidmem[bmp_ofs_y+xc];
+ tileptr = &tile[tile_ofs_y];
+ memmove(tileptr, vidptr, X_TILESIZE<<1);
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ }
+ else /* Update 8bpp mode */
+ {
+ Bit8u *vidmem = &BX_VGA_THIS s.vbe_memory[BX_VGA_THIS s.vbe_virtual_start];
+ Bit8u *tile = BX_VGA_THIS s.tile;
+ Bit16u width = BX_VGA_THIS s.vbe_virtual_xres;
+
+ Bit8u *vidptr, *tileptr;
+
+ iWidth=BX_VGA_THIS s.vbe_xres;
+ iHeight=BX_VGA_THIS s.vbe_yres;
+
+ for (yc=0, yti = 0; yc<iHeight; yc+=Y_TILESIZE, yti++)
+ {
+ for (xc=0, xti = 0; xc<iWidth; xc+=X_TILESIZE, xti++)
+ {
+ // If the tile has not been updated, copy it into the tile buffer for update
+ if (GET_TILE_UPDATED (xti, yti)) {
+ for (r=0; r<Y_TILESIZE; r++) {
+ // actual video y coord is tile_y + y
+ pixely = yc + r;
+ // calc offsets into video and tile memory
+ bmp_ofs_y = pixely*width;
+ tile_ofs_y = r*X_TILESIZE;
+ // get offsets so that we do less calc in the inner loop
+ vidptr = &vidmem[bmp_ofs_y+xc];
+ tileptr = &tile[tile_ofs_y];
+ memmove(tileptr, vidptr, X_TILESIZE);
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ }
+
+ old_iWidth = iWidth;
+ old_iHeight = iHeight;
+ BX_VGA_THIS s.vga_mem_updated = 0;
+ // after a vbe display update, don't try to do any 'normal vga' updates anymore
+ return;
+ }
+#endif
+ // fields that effect the way video memory is serialized into screen output:
+ // GRAPHICS CONTROLLER:
+ // BX_VGA_THIS s.graphics_ctrl.shift_reg:
+ // 0: output data in standard VGA format or CGA-compatible 640x200 2 color
+ // graphics mode (mode 6)
+ // 1: output data in CGA-compatible 320x200 4 color graphics mode
+ // (modes 4 & 5)
+ // 2: output data 8 bits at a time from the 4 bit planes
+ // (mode 13 and variants like modeX)
+
+ // if (BX_VGA_THIS s.vga_mem_updated==0 || BX_VGA_THIS s.attribute_ctrl.video_enabled == 0)
+
+ if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) {
+ Bit8u color;
+ unsigned bit_no, r, c, x, y;
+ unsigned long byte_offset, start_addr;
+ unsigned xc, yc, xti, yti;
+
+ start_addr = (BX_VGA_THIS s.CRTC.reg[0x0c] << 8) | BX_VGA_THIS s.CRTC.reg[0x0d];
+
+//BX_DEBUG(("update: shiftreg=%u, chain4=%u, mapping=%u",
+// (unsigned) BX_VGA_THIS s.graphics_ctrl.shift_reg,
+// (unsigned) BX_VGA_THIS s.sequencer.chain_four,
+// (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping);
+
+ determine_screen_dimensions(&iHeight, &iWidth);
+ if( (iWidth != old_iWidth) || (iHeight != old_iHeight) || (old_BPP > 8) ) {
+ bx_gui->dimension_update(iWidth, iHeight);
+ old_iWidth = iWidth;
+ old_iHeight = iHeight;
+ old_BPP = 8;
+ }
+
+ switch ( BX_VGA_THIS s.graphics_ctrl.shift_reg ) {
+
+ case 0:
+ Bit8u attribute, palette_reg_val, DAC_regno;
+ unsigned long line_compare;
+
+ if (BX_VGA_THIS s.graphics_ctrl.memory_mapping == 3) { // CGA 640x200x2
+
+ for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
+ for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) {
+ if (GET_TILE_UPDATED (xti, yti)) {
+ for (r=0; r<Y_TILESIZE; r++) {
+ y = yc + r;
+ if (BX_VGA_THIS s.y_doublescan) y >>= 1;
+ for (c=0; c<X_TILESIZE; c++) {
+
+ x = xc + c;
+ /* 0 or 0x2000 */
+ byte_offset = start_addr + ((y & 1) << 13);
+ /* to the start of the line */
+ byte_offset += (320 / 4) * (y / 2);
+ /* to the byte start */
+ byte_offset += (x / 8);
+
+ bit_no = 7 - (x % 8);
+ palette_reg_val = (((BX_VGA_THIS s.vga_memory[byte_offset]) >> bit_no) & 1);
+ DAC_regno = BX_VGA_THIS s.attribute_ctrl.palette_reg[palette_reg_val];
+ BX_VGA_THIS s.tile[r*X_TILESIZE + c] = DAC_regno;
+ }
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ } else { // output data in serial fashion with each display plane
+ // output on its associated serial output. Standard EGA/VGA format
+
+ line_compare = BX_VGA_THIS s.line_compare;
+ if (BX_VGA_THIS s.y_doublescan) line_compare >>= 1;
+
+ for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
+ for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) {
+ if (GET_TILE_UPDATED (xti, yti)) {
+ for (r=0; r<Y_TILESIZE; r++) {
+ y = yc + r;
+ if (BX_VGA_THIS s.y_doublescan) y >>= 1;
+ for (c=0; c<X_TILESIZE; c++) {
+ x = xc + c;
+ if (BX_VGA_THIS s.x_dotclockdiv2) x >>= 1;
+ bit_no = 7 - (x % 8);
+ if (y > line_compare) {
+ byte_offset = x / 8 +
+ ((y - line_compare - 1) * BX_VGA_THIS s.line_offset);
+ } else {
+ byte_offset = start_addr + x / 8 +
+ (y * BX_VGA_THIS s.line_offset);
+ }
+ attribute =
+ (((BX_VGA_THIS s.vga_memory[0*65536 + byte_offset] >> bit_no) & 0x01) << 0) |
+ (((BX_VGA_THIS s.vga_memory[1*65536 + byte_offset] >> bit_no) & 0x01) << 1) |
+ (((BX_VGA_THIS s.vga_memory[2*65536 + byte_offset] >> bit_no) & 0x01) << 2) |
+ (((BX_VGA_THIS s.vga_memory[3*65536 + byte_offset] >> bit_no) & 0x01) << 3);
+
+ attribute &= BX_VGA_THIS s.attribute_ctrl.color_plane_enable;
+ // undocumented feature ???: colors 0..7 high intensity, colors 8..15 blinking
+ // using low/high intensity. Blinking is not implemented yet.
+ if (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.blink_intensity) attribute ^= 0x08;
+ palette_reg_val = BX_VGA_THIS s.attribute_ctrl.palette_reg[attribute];
+ if (BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size) {
+ // use 4 lower bits from palette register
+ // use 4 higher bits from color select register
+ // 16 banks of 16-color registers
+ DAC_regno = (palette_reg_val & 0x0f) |
+ (BX_VGA_THIS s.attribute_ctrl.color_select << 4);
+ }
+ else {
+ // use 6 lower bits from palette register
+ // use 2 higher bits from color select register
+ // 4 banks of 64-color registers
+ DAC_regno = (palette_reg_val & 0x3f) |
+ ((BX_VGA_THIS s.attribute_ctrl.color_select & 0x0c) << 4);
+ }
+ // DAC_regno &= video DAC mask register ???
+
+ BX_VGA_THIS s.tile[r*X_TILESIZE + c] = DAC_regno;
+ }
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ }
+ break; // case 0
+
+ case 1: // output the data in a CGA-compatible 320x200 4 color graphics
+ // mode. (modes 4 & 5)
+
+ /* CGA 320x200x4 start */
+
+ for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
+ for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) {
+ if (GET_TILE_UPDATED (xti, yti)) {
+ for (r=0; r<Y_TILESIZE; r++) {
+ y = yc + r;
+ if (BX_VGA_THIS s.y_doublescan) y >>= 1;
+ for (c=0; c<X_TILESIZE; c++) {
+
+ x = xc + c;
+ if (BX_VGA_THIS s.x_dotclockdiv2) x >>= 1;
+ /* 0 or 0x2000 */
+ byte_offset = start_addr + ((y & 1) << 13);
+ /* to the start of the line */
+ byte_offset += (320 / 4) * (y / 2);
+ /* to the byte start */
+ byte_offset += (x / 4);
+
+ attribute = 6 - 2*(x % 4);
+ palette_reg_val = (BX_VGA_THIS s.vga_memory[byte_offset]) >> attribute;
+ palette_reg_val &= 3;
+ DAC_regno = BX_VGA_THIS s.attribute_ctrl.palette_reg[palette_reg_val];
+ BX_VGA_THIS s.tile[r*X_TILESIZE + c] = DAC_regno;
+ }
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ /* CGA 320x200x4 end */
+
+ break; // case 1
+
+ case 2: // output the data eight bits at a time from the 4 bit plane
+ // (format for VGA mode 13 hex)
+
+ if ( BX_VGA_THIS s.sequencer.chain_four ) {
+ unsigned long pixely, pixelx, plane;
+ // bx_vga_dump_status();
+
+ if (BX_VGA_THIS s.misc_output.select_high_bank != 1)
+ BX_PANIC(("update: select_high_bank != 1"));
+
+ for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
+ for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) {
+ if (GET_TILE_UPDATED (xti, yti)) {
+ for (r=0; r<Y_TILESIZE; r++) {
+ pixely = yc + r;
+ if (BX_VGA_THIS s.y_doublescan) pixely >>= 1;
+ for (c=0; c<X_TILESIZE; c++) {
+ pixelx = (xc + c) >> 1;
+ plane = (pixelx % 4);
+ byte_offset = start_addr + (plane * 65536) +
+ (pixely * BX_VGA_THIS s.line_offset) + (pixelx & ~0x03);
+ color = BX_VGA_THIS s.vga_memory[byte_offset];
+ BX_VGA_THIS s.tile[r*X_TILESIZE + c] = color;
+ }
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ }
+
+ else { // chain_four == 0, modeX
+ unsigned long pixely, pixelx, plane;
+
+ for (yc=0, yti=0; yc<iHeight; yc+=Y_TILESIZE, yti++) {
+ for (xc=0, xti=0; xc<iWidth; xc+=X_TILESIZE, xti++) {
+ if (GET_TILE_UPDATED (xti, yti)) {
+ for (r=0; r<Y_TILESIZE; r++) {
+ pixely = yc + r;
+ if (BX_VGA_THIS s.y_doublescan) pixely >>= 1;
+ for (c=0; c<X_TILESIZE; c++) {
+ pixelx = (xc + c) >> 1;
+ plane = (pixelx % 4);
+ byte_offset = (plane * 65536) +
+ (pixely * BX_VGA_THIS s.line_offset)
+ + (pixelx >> 2);
+ color = BX_VGA_THIS s.vga_memory[start_addr + byte_offset];
+ BX_VGA_THIS s.tile[r*X_TILESIZE + c] = color;
+ }
+ }
+ SET_TILE_UPDATED (xti, yti, 0);
+ bx_gui->graphics_tile_update(BX_VGA_THIS s.tile, xc, yc);
+ }
+ }
+ }
+ }
+ break; // case 2
+
+ default:
+ BX_PANIC(("update: shift_reg == %u", (unsigned)
+ BX_VGA_THIS s.graphics_ctrl.shift_reg ));
+ }
+
+ BX_VGA_THIS s.vga_mem_updated = 0;
+ return;
+ }
+
+ else { // text mode
+ unsigned long start_address;
+ unsigned long cursor_address, cursor_x, cursor_y;
+ bx_vga_tminfo_t tm_info;
+
+
+ tm_info.cs_start = BX_VGA_THIS s.CRTC.reg[0x0a] & 0x3f;
+ tm_info.cs_end = BX_VGA_THIS s.CRTC.reg[0x0b] & 0x1f;
+ tm_info.line_offset = BX_VGA_THIS s.CRTC.reg[0x13] << 2;
+ tm_info.line_compare = BX_VGA_THIS s.line_compare;
+ tm_info.h_panning = BX_VGA_THIS s.attribute_ctrl.horiz_pel_panning & 0x0f;
+ tm_info.v_panning = BX_VGA_THIS s.CRTC.reg[0x08] & 0x1f;
+ tm_info.line_graphics = BX_VGA_THIS s.attribute_ctrl.mode_ctrl.enable_line_graphics;
+ if ((BX_VGA_THIS s.sequencer.reg1 & 0x01) == 0) {
+ if (tm_info.h_panning == 8)
+ tm_info.h_panning = 0;
+ else
+ tm_info.h_panning++;
+ }
+
+ switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) {
+ case 0: // 128K @ A0000
+ case 1: // 64K @ A0000
+ iWidth = 8*80; // TODO: should use font size
+ iHeight = 16*25;
+ if( (iWidth != old_iWidth) || (iHeight != old_iHeight) || (old_BPP > 8) )
+ {
+ bx_gui->dimension_update(iWidth, iHeight, 16, 8);
+ old_iWidth = iWidth;
+ old_iHeight = iHeight;
+ old_BPP = 8;
+ }
+ /* pass old text snapshot & new VGA memory contents */
+ start_address = 0x0;
+ cursor_address = 2*((BX_VGA_THIS s.CRTC.reg[0x0e] << 8) |
+ BX_VGA_THIS s.CRTC.reg[0x0f]);
+ if (cursor_address < start_address) {
+ cursor_x = 0xffff;
+ cursor_y = 0xffff;
+ }
+ else {
+ cursor_x = ((cursor_address - start_address)/2) % 80;
+ cursor_y = ((cursor_address - start_address)/2) / 80;
+ }
+ bx_gui->text_update(BX_VGA_THIS s.text_snapshot,
+ &BX_VGA_THIS s.vga_memory[start_address],
+ cursor_x, cursor_y, tm_info, 25);
+ // screen updated, copy new VGA memory contents into text snapshot
+ memcpy(BX_VGA_THIS s.text_snapshot,
+ &BX_VGA_THIS s.vga_memory[start_address],
+ 2*80*25);
+ BX_VGA_THIS s.vga_mem_updated = 0;
+ break;
+
+ case 2: // B0000 .. B7FFF
+ case 3: // B8000 .. BFFFF
+ unsigned VDE, MSL, rows, cWidth;
+
+ // Verticle Display End: find out how many lines are displayed
+ VDE = BX_VGA_THIS s.vertical_display_end;
+ // Maximum Scan Line: height of character cell
+ MSL = BX_VGA_THIS s.CRTC.reg[0x09] & 0x1f;
+ if (MSL == 0) {
+ //BX_ERROR(("character height = 1, skipping text update"));
+ return;
+ }
+ if ((MSL == 1) && (BX_VGA_THIS s.CRTC.reg[0x06] == 100)) {
+ // emulated CGA graphics mode 160x100x16 colors
+ MSL = 3;
+ rows = 100;
+ cWidth = 8;
+ iWidth = cWidth * BX_VGA_THIS s.CRTC.reg[1];
+ iHeight = 400;
+ } else {
+ rows = (VDE+1)/(MSL+1);
+ if (rows > BX_MAX_TEXT_LINES)
+ BX_PANIC(("text rows>%d: %d",BX_MAX_TEXT_LINES,rows));
+ cWidth = ((BX_VGA_THIS s.sequencer.reg1 & 0x01) == 1) ? 8 : 9;
+ iWidth = cWidth * (BX_VGA_THIS s.CRTC.reg[1] + 1);
+ iHeight = VDE+1;
+ }
+ if( (iWidth != old_iWidth) || (iHeight != old_iHeight) || (MSL != old_MSL) || (old_BPP > 8) )
+ {
+ bx_gui->dimension_update(iWidth, iHeight, MSL+1, cWidth);
+ old_iWidth = iWidth;
+ old_iHeight = iHeight;
+ old_MSL = MSL;
+ old_BPP = 8;
+ }
+ // pass old text snapshot & new VGA memory contents
+ start_address = 2*((BX_VGA_THIS s.CRTC.reg[12] << 8) + BX_VGA_THIS s.CRTC.reg[13]);
+ cursor_address = 2*((BX_VGA_THIS s.CRTC.reg[0x0e] << 8) |
+ BX_VGA_THIS s.CRTC.reg[0x0f]);
+ if (cursor_address < start_address) {
+ cursor_x = 0xffff;
+ cursor_y = 0xffff;
+ }
+ else {
+ cursor_x = ((cursor_address - start_address)/2) % (iWidth/cWidth);
+ cursor_y = ((cursor_address - start_address)/2) / (iWidth/cWidth);
+ }
+ bx_gui->text_update(BX_VGA_THIS s.text_snapshot,
+ &BX_VGA_THIS s.vga_memory[start_address],
+ cursor_x, cursor_y, tm_info, rows);
+ // screen updated, copy new VGA memory contents into text snapshot
+ memcpy(BX_VGA_THIS s.text_snapshot,
+ &BX_VGA_THIS s.vga_memory[start_address],
+ 2*80*rows);
+ BX_VGA_THIS s.vga_mem_updated = 0;
+ break;
+ default:
+ BX_DEBUG(("update(): color text mode: mem map is %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping));
+ }
+ }
+}
+
+
+ Bit8u
+bx_vga_c::mem_read(Bit32u addr)
+{
+ Bit32u offset;
+
+#if BX_SUPPORT_VBE
+ // if in a vbe enabled mode, read from the vbe_memory
+ if ((BX_VGA_THIS s.vbe_enabled) && (BX_VGA_THIS s.vbe_bpp != VBE_DISPI_BPP_4))
+ {
+ return vbe_mem_read(addr);
+ }
+#endif
+
+#if defined(VGA_TRACE_FEATURE)
+// BX_DEBUG(("8-bit memory read from %08x", addr));
+#endif
+
+#ifdef __OS2__
+
+#if BX_PLUGINS
+#error Fix the code for plugins
+#endif
+
+ if ( bx_options.videomode == BX_VIDEO_DIRECT )
+ {
+ char value;
+
+ value = devices->mem->video[addr-0xA0000];
+
+ return value;
+ }
+#endif
+
+ switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) {
+ case 1: // 0xA0000 .. 0xAFFFF
+ if (addr > 0xAFFFF) return 0xff;
+ offset = addr - 0xA0000;
+ break;
+ case 2: // 0xB0000 .. 0xB7FFF
+ if ((addr < 0xB0000) || (addr > 0xB7FFF)) return 0xff;
+ return BX_VGA_THIS s.vga_memory[addr - 0xB0000];
+ break;
+ case 3: // 0xB8000 .. 0xBFFFF
+ if (addr < 0xB8000) return 0xff;
+ return BX_VGA_THIS s.vga_memory[addr - 0xB8000];
+ break;
+ default: // 0xA0000 .. 0xBFFFF
+ return BX_VGA_THIS s.vga_memory[addr - 0xA0000];
+ }
+
+ // addr between 0xA0000 and 0xAFFFF
+ if ( BX_VGA_THIS s.sequencer.chain_four ) {
+
+ // Mode 13h: 320 x 200 256 color mode: chained pixel representation
+ return BX_VGA_THIS s.vga_memory[(offset & ~0x03) + (offset % 4)*65536];
+ }
+
+ /* addr between 0xA0000 and 0xAFFFF */
+ switch (BX_VGA_THIS s.graphics_ctrl.read_mode) {
+ case 0: /* read mode 0 */
+ BX_VGA_THIS s.graphics_ctrl.latch[0] = BX_VGA_THIS s.vga_memory[ offset];
+ BX_VGA_THIS s.graphics_ctrl.latch[1] = BX_VGA_THIS s.vga_memory[1*65536 + offset];
+ BX_VGA_THIS s.graphics_ctrl.latch[2] = BX_VGA_THIS s.vga_memory[2*65536 + offset];
+ BX_VGA_THIS s.graphics_ctrl.latch[3] = BX_VGA_THIS s.vga_memory[3*65536 + offset];
+ return(BX_VGA_THIS s.graphics_ctrl.latch[BX_VGA_THIS s.graphics_ctrl.read_map_select]);
+ break;
+
+ case 1: /* read mode 1 */
+ {
+ Bit8u color_compare, color_dont_care;
+ Bit8u latch0, latch1, latch2, latch3, retval;
+
+ color_compare = BX_VGA_THIS s.graphics_ctrl.color_compare & 0x0f;
+ color_dont_care = BX_VGA_THIS s.graphics_ctrl.color_dont_care & 0x0f;
+ latch0 = BX_VGA_THIS s.graphics_ctrl.latch[0] = BX_VGA_THIS s.vga_memory[ offset];
+ latch1 = BX_VGA_THIS s.graphics_ctrl.latch[1] = BX_VGA_THIS s.vga_memory[1*65536 + offset];
+ latch2 = BX_VGA_THIS s.graphics_ctrl.latch[2] = BX_VGA_THIS s.vga_memory[2*65536 + offset];
+ latch3 = BX_VGA_THIS s.graphics_ctrl.latch[3] = BX_VGA_THIS s.vga_memory[3*65536 + offset];
+
+ latch0 ^= ccdat[color_compare][0];
+ latch1 ^= ccdat[color_compare][1];
+ latch2 ^= ccdat[color_compare][2];
+ latch3 ^= ccdat[color_compare][3];
+
+ latch0 &= ccdat[color_dont_care][0];
+ latch1 &= ccdat[color_dont_care][1];
+ latch2 &= ccdat[color_dont_care][2];
+ latch3 &= ccdat[color_dont_care][3];
+
+ retval = ~(latch0 | latch1 | latch2 | latch3);
+
+ return retval;
+ }
+ break;
+ default:
+ return 0;
+ }
+}
+
+ void
+bx_vga_c::mem_write(Bit32u addr, Bit8u value)
+{
+ Bit32u offset;
+ Bit8u new_val[4];
+ unsigned start_addr;
+
+#if BX_SUPPORT_VBE
+ // if in a vbe enabled mode, write to the vbe_memory
+ if ((BX_VGA_THIS s.vbe_enabled) && (BX_VGA_THIS s.vbe_bpp != VBE_DISPI_BPP_4))
+ {
+ vbe_mem_write(addr,value);
+ return;
+ }
+#endif
+
+#if defined(VGA_TRACE_FEATURE)
+// BX_DEBUG(("8-bit memory write to %08x = %02x", addr, value));
+#endif
+
+#ifdef __OS2__
+
+#if BX_PLUGINS
+#error Fix the code for plugins
+#endif
+
+ if ( bx_options.videomode == BX_VIDEO_DIRECT )
+ {
+ devices->mem->video[addr-0xA0000] = value;
+
+ return;
+ }
+#endif
+
+ switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) {
+ case 1: // 0xA0000 .. 0xAFFFF
+ if (addr > 0xAFFFF) return;
+ offset = addr - 0xA0000;
+ break;
+ case 2: // 0xB0000 .. 0xB7FFF
+ if ((addr < 0xB0000) || (addr > 0xB7FFF)) return;
+ offset = addr - 0xB0000;
+ break;
+ case 3: // 0xB8000 .. 0xBFFFF
+ if (addr < 0xB8000) return;
+ offset = addr - 0xB8000;
+ break;
+ default: // 0xA0000 .. 0xBFFFF
+ offset = addr - 0xA0000;
+ }
+
+ start_addr = (BX_VGA_THIS s.CRTC.reg[0x0c] << 8) | BX_VGA_THIS s.CRTC.reg[0x0d];
+
+ if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) {
+ if (BX_VGA_THIS s.graphics_ctrl.memory_mapping == 3) { // 0xB8000 .. 0xBFFFF
+ unsigned x_tileno, x_tileno2, y_tileno;
+
+ /* CGA 320x200x4 / 640x200x2 start */
+ BX_VGA_THIS s.vga_memory[offset] = value;
+ offset -= start_addr;
+ if (offset>=0x2000) {
+ y_tileno = offset - 0x2000;
+ y_tileno /= (320/4);
+ y_tileno <<= 1; //2 * y_tileno;
+ y_tileno++;
+ x_tileno = (offset - 0x2000) % (320/4);
+ x_tileno <<= 2; //*= 4;
+ } else {
+ y_tileno = offset / (320/4);
+ y_tileno <<= 1; //2 * y_tileno;
+ x_tileno = offset % (320/4);
+ x_tileno <<= 2; //*=4;
+ }
+ x_tileno2=x_tileno;
+ if (BX_VGA_THIS s.graphics_ctrl.shift_reg==0) {
+ x_tileno*=2;
+ x_tileno2+=7;
+ } else {
+ x_tileno2+=3;
+ }
+ if (BX_VGA_THIS s.x_dotclockdiv2) {
+ x_tileno/=(X_TILESIZE/2);
+ x_tileno2/=(X_TILESIZE/2);
+ } else {
+ x_tileno/=X_TILESIZE;
+ x_tileno2/=X_TILESIZE;
+ }
+ if (BX_VGA_THIS s.y_doublescan) {
+ y_tileno/=(Y_TILESIZE/2);
+ } else {
+ y_tileno/=Y_TILESIZE;
+ }
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ SET_TILE_UPDATED (x_tileno, y_tileno, 1);
+ if (x_tileno2!=x_tileno) {
+ SET_TILE_UPDATED (x_tileno2, y_tileno, 1);
+ }
+ return;
+ /* CGA 320x200x4 / 640x200x2 end */
+ }
+ else if (BX_VGA_THIS s.graphics_ctrl.memory_mapping != 1) {
+
+ BX_PANIC(("mem_write: graphics: mapping = %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping));
+ return;
+ }
+
+ if ( BX_VGA_THIS s.sequencer.chain_four ) {
+ unsigned x_tileno, y_tileno;
+
+ // 320 x 200 256 color mode: chained pixel representation
+ BX_VGA_THIS s.vga_memory[(offset & ~0x03) + (offset % 4)*65536] = value;
+ offset -= start_addr;
+ x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE/2);
+ if (BX_VGA_THIS s.y_doublescan) {
+ y_tileno = (offset / BX_VGA_THIS s.line_offset) / (Y_TILESIZE/2);
+ } else {
+ y_tileno = (offset / BX_VGA_THIS s.line_offset) / Y_TILESIZE;
+ }
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ SET_TILE_UPDATED (x_tileno, y_tileno, 1);
+ return;
+ }
+
+ }
+
+ /* addr between 0xA0000 and 0xAFFFF */
+ switch (BX_VGA_THIS s.graphics_ctrl.write_mode) {
+ unsigned i;
+
+ case 0: /* write mode 0 */
+ {
+ const Bit8u bitmask = BX_VGA_THIS s.graphics_ctrl.bitmask;
+ const Bit8u set_reset = BX_VGA_THIS s.graphics_ctrl.set_reset;
+ const Bit8u enable_set_reset = BX_VGA_THIS s.graphics_ctrl.enable_set_reset;
+ /* perform rotate on CPU data in case its needed */
+ if (BX_VGA_THIS s.graphics_ctrl.data_rotate) {
+ value = (value >> BX_VGA_THIS s.graphics_ctrl.data_rotate) |
+ (value << (8 - BX_VGA_THIS s.graphics_ctrl.data_rotate));
+ }
+ new_val[0] = BX_VGA_THIS s.graphics_ctrl.latch[0] & ~bitmask;
+ new_val[1] = BX_VGA_THIS s.graphics_ctrl.latch[1] & ~bitmask;
+ new_val[2] = BX_VGA_THIS s.graphics_ctrl.latch[2] & ~bitmask;
+ new_val[3] = BX_VGA_THIS s.graphics_ctrl.latch[3] & ~bitmask;
+ switch (BX_VGA_THIS s.graphics_ctrl.raster_op) {
+ case 0: // replace
+ new_val[0] |= ((enable_set_reset & 1)
+ ? ((set_reset & 1) ? bitmask : 0)
+ : (value & bitmask));
+ new_val[1] |= ((enable_set_reset & 2)
+ ? ((set_reset & 2) ? bitmask : 0)
+ : (value & bitmask));
+ new_val[2] |= ((enable_set_reset & 4)
+ ? ((set_reset & 4) ? bitmask : 0)
+ : (value & bitmask));
+ new_val[3] |= ((enable_set_reset & 8)
+ ? ((set_reset & 8) ? bitmask : 0)
+ : (value & bitmask));
+ break;
+ case 1: // AND
+ new_val[0] |= ((enable_set_reset & 1)
+ ? ((set_reset & 1)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask)
+ : 0)
+ : (value & BX_VGA_THIS s.graphics_ctrl.latch[0]) & bitmask);
+ new_val[1] |= ((enable_set_reset & 2)
+ ? ((set_reset & 2)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask)
+ : 0)
+ : (value & BX_VGA_THIS s.graphics_ctrl.latch[1]) & bitmask);
+ new_val[2] |= ((enable_set_reset & 4)
+ ? ((set_reset & 4)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask)
+ : 0)
+ : (value & BX_VGA_THIS s.graphics_ctrl.latch[2]) & bitmask);
+ new_val[3] |= ((enable_set_reset & 8)
+ ? ((set_reset & 8)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask)
+ : 0)
+ : (value & BX_VGA_THIS s.graphics_ctrl.latch[3]) & bitmask);
+ break;
+ case 2: // OR
+ new_val[0]
+ |= ((enable_set_reset & 1)
+ ? ((set_reset & 1)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask))
+ : ((value | BX_VGA_THIS s.graphics_ctrl.latch[0]) & bitmask));
+ new_val[1]
+ |= ((enable_set_reset & 2)
+ ? ((set_reset & 2)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask))
+ : ((value | BX_VGA_THIS s.graphics_ctrl.latch[1]) & bitmask));
+ new_val[2]
+ |= ((enable_set_reset & 4)
+ ? ((set_reset & 4)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask))
+ : ((value | BX_VGA_THIS s.graphics_ctrl.latch[2]) & bitmask));
+ new_val[3]
+ |= ((enable_set_reset & 8)
+ ? ((set_reset & 8)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask))
+ : ((value | BX_VGA_THIS s.graphics_ctrl.latch[3]) & bitmask));
+ break;
+ case 3: // XOR
+ new_val[0]
+ |= ((enable_set_reset & 1)
+ ? ((set_reset & 1)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask))
+ : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[0]) & bitmask);
+ new_val[1]
+ |= ((enable_set_reset & 2)
+ ? ((set_reset & 2)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask))
+ : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[1]) & bitmask);
+ new_val[2]
+ |= ((enable_set_reset & 4)
+ ? ((set_reset & 4)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask))
+ : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[2]) & bitmask);
+ new_val[3]
+ |= ((enable_set_reset & 8)
+ ? ((set_reset & 8)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask))
+ : (value ^ BX_VGA_THIS s.graphics_ctrl.latch[3]) & bitmask);
+ break;
+ default:
+ BX_PANIC(("vga_mem_write: write mode 0: op = %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.raster_op));
+ }
+ }
+ break;
+
+ case 1: /* write mode 1 */
+ for (i=0; i<4; i++ ) {
+ new_val[i] = BX_VGA_THIS s.graphics_ctrl.latch[i];
+ }
+ break;
+
+ case 2: /* write mode 2 */
+ {
+ const Bit8u bitmask = BX_VGA_THIS s.graphics_ctrl.bitmask;
+
+ new_val[0] = BX_VGA_THIS s.graphics_ctrl.latch[0] & ~bitmask;
+ new_val[1] = BX_VGA_THIS s.graphics_ctrl.latch[1] & ~bitmask;
+ new_val[2] = BX_VGA_THIS s.graphics_ctrl.latch[2] & ~bitmask;
+ new_val[3] = BX_VGA_THIS s.graphics_ctrl.latch[3] & ~bitmask;
+ switch (BX_VGA_THIS s.graphics_ctrl.raster_op) {
+ case 0: // write
+ new_val[0] |= (value & 1) ? bitmask : 0;
+ new_val[1] |= (value & 2) ? bitmask : 0;
+ new_val[2] |= (value & 4) ? bitmask : 0;
+ new_val[3] |= (value & 8) ? bitmask : 0;
+ break;
+ case 1: // AND
+ new_val[0] |= (value & 1)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask)
+ : 0;
+ new_val[1] |= (value & 2)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask)
+ : 0;
+ new_val[2] |= (value & 4)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask)
+ : 0;
+ new_val[3] |= (value & 8)
+ ? (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask)
+ : 0;
+ break;
+ case 2: // OR
+ new_val[0] |= (value & 1)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask);
+ new_val[1] |= (value & 2)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask);
+ new_val[2] |= (value & 4)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask);
+ new_val[3] |= (value & 8)
+ ? bitmask
+ : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask);
+ break;
+ case 3: // XOR
+ new_val[0] |= (value & 1)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[0] & bitmask);
+ new_val[1] |= (value & 2)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[1] & bitmask);
+ new_val[2] |= (value & 4)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[2] & bitmask);
+ new_val[3] |= (value & 8)
+ ? (~BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask)
+ : (BX_VGA_THIS s.graphics_ctrl.latch[3] & bitmask);
+ break;
+ }
+ }
+ break;
+
+ case 3: /* write mode 3 */
+ {
+ const Bit8u bitmask = BX_VGA_THIS s.graphics_ctrl.bitmask & value;
+ const Bit8u set_reset = BX_VGA_THIS s.graphics_ctrl.set_reset;
+
+ /* perform rotate on CPU data */
+ if (BX_VGA_THIS s.graphics_ctrl.data_rotate) {
+ value = (value >> BX_VGA_THIS s.graphics_ctrl.data_rotate) |
+ (value << (8 - BX_VGA_THIS s.graphics_ctrl.data_rotate));
+ }
+ new_val[0] = BX_VGA_THIS s.graphics_ctrl.latch[0] & ~bitmask;
+ new_val[1] = BX_VGA_THIS s.graphics_ctrl.latch[1] & ~bitmask;
+ new_val[2] = BX_VGA_THIS s.graphics_ctrl.latch[2] & ~bitmask;
+ new_val[3] = BX_VGA_THIS s.graphics_ctrl.latch[3] & ~bitmask;
+
+ value &= bitmask;
+
+ switch (BX_VGA_THIS s.graphics_ctrl.raster_op) {
+ case 0: // write
+ new_val[0] |= (set_reset & 1) ? value : 0;
+ new_val[1] |= (set_reset & 2) ? value : 0;
+ new_val[2] |= (set_reset & 4) ? value : 0;
+ new_val[3] |= (set_reset & 8) ? value : 0;
+ break;
+ case 1: // AND
+ new_val[0] |= ((set_reset & 1) ? value : 0)
+ & BX_VGA_THIS s.graphics_ctrl.latch[0];
+ new_val[1] |= ((set_reset & 2) ? value : 0)
+ & BX_VGA_THIS s.graphics_ctrl.latch[1];
+ new_val[2] |= ((set_reset & 4) ? value : 0)
+ & BX_VGA_THIS s.graphics_ctrl.latch[2];
+ new_val[3] |= ((set_reset & 8) ? value : 0)
+ & BX_VGA_THIS s.graphics_ctrl.latch[3];
+ break;
+ case 2: // OR
+ new_val[0] |= ((set_reset & 1) ? value : 0)
+ | BX_VGA_THIS s.graphics_ctrl.latch[0];
+ new_val[1] |= ((set_reset & 2) ? value : 0)
+ | BX_VGA_THIS s.graphics_ctrl.latch[1];
+ new_val[2] |= ((set_reset & 4) ? value : 0)
+ | BX_VGA_THIS s.graphics_ctrl.latch[2];
+ new_val[3] |= ((set_reset & 8) ? value : 0)
+ | BX_VGA_THIS s.graphics_ctrl.latch[3];
+ break;
+ case 3: // XOR
+ new_val[0] |= ((set_reset & 1) ? value : 0)
+ ^ BX_VGA_THIS s.graphics_ctrl.latch[0];
+ new_val[1] |= ((set_reset & 2) ? value : 0)
+ ^ BX_VGA_THIS s.graphics_ctrl.latch[1];
+ new_val[2] |= ((set_reset & 4) ? value : 0)
+ ^ BX_VGA_THIS s.graphics_ctrl.latch[2];
+ new_val[3] |= ((set_reset & 8) ? value : 0)
+ ^ BX_VGA_THIS s.graphics_ctrl.latch[3];
+ break;
+ }
+ }
+ break;
+
+ default:
+ BX_PANIC(("vga_mem_write: write mode %u ?",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.write_mode));
+ }
+
+ if (BX_VGA_THIS s.sequencer.map_mask & 0x0f) {
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ if (BX_VGA_THIS s.sequencer.map_mask_bit[0])
+ BX_VGA_THIS s.vga_memory[0*65536 + offset] = new_val[0];
+ if (BX_VGA_THIS s.sequencer.map_mask_bit[1])
+ BX_VGA_THIS s.vga_memory[1*65536 + offset] = new_val[1];
+ if (BX_VGA_THIS s.sequencer.map_mask_bit[2]) {
+ if ((!BX_VGA_THIS s.graphics_ctrl.graphics_alpha) &&
+ ((offset & 0xe000) == BX_VGA_THIS s.charmap_address)) {
+ bx_gui->set_text_charbyte((offset & 0x1fff), new_val[2]);
+ }
+ BX_VGA_THIS s.vga_memory[2*65536 + offset] = new_val[2];
+ }
+ if (BX_VGA_THIS s.sequencer.map_mask_bit[3])
+ BX_VGA_THIS s.vga_memory[3*65536 + offset] = new_val[3];
+
+ unsigned x_tileno, y_tileno;
+
+ if (BX_VGA_THIS s.graphics_ctrl.shift_reg == 2) {
+ offset -= start_addr;
+ x_tileno = (offset % BX_VGA_THIS s.line_offset) * 4 / (X_TILESIZE / 2);
+ if (BX_VGA_THIS s.y_doublescan) {
+ y_tileno = (offset / BX_VGA_THIS s.line_offset) / (Y_TILESIZE / 2);
+ } else {
+ y_tileno = (offset / BX_VGA_THIS s.line_offset) / Y_TILESIZE;
+ }
+ SET_TILE_UPDATED (x_tileno, y_tileno, 1);
+ } else {
+ if (BX_VGA_THIS s.line_compare < BX_VGA_THIS s.vertical_display_end) {
+ if (BX_VGA_THIS s.x_dotclockdiv2) {
+ x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 16);
+ } else {
+ x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 8);
+ }
+ if (BX_VGA_THIS s.y_doublescan) {
+ y_tileno = ((offset / BX_VGA_THIS s.line_offset) * 2 + BX_VGA_THIS s.line_compare + 1) / Y_TILESIZE;
+ } else {
+ y_tileno = ((offset / BX_VGA_THIS s.line_offset) + BX_VGA_THIS s.line_compare + 1) / Y_TILESIZE;
+ }
+ SET_TILE_UPDATED (x_tileno, y_tileno, 1);
+ }
+ if (offset >= start_addr) {
+ offset -= start_addr;
+ if (BX_VGA_THIS s.x_dotclockdiv2) {
+ x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 16);
+ } else {
+ x_tileno = (offset % BX_VGA_THIS s.line_offset) / (X_TILESIZE / 8);
+ }
+ if (BX_VGA_THIS s.y_doublescan) {
+ y_tileno = (offset / BX_VGA_THIS s.line_offset) / (Y_TILESIZE / 2);
+ } else {
+ y_tileno = (offset / BX_VGA_THIS s.line_offset) / Y_TILESIZE;
+ }
+ SET_TILE_UPDATED (x_tileno, y_tileno, 1);
+ }
+ }
+ }
+}
+
+ void
+bx_vga_c::get_text_snapshot(Bit8u **text_snapshot, unsigned *txHeight,
+ unsigned *txWidth)
+{
+ unsigned VDE, MSL;
+
+ if (!BX_VGA_THIS s.graphics_ctrl.graphics_alpha) {
+ *text_snapshot = &BX_VGA_THIS s.text_snapshot[0];
+ VDE = BX_VGA_THIS s.vertical_display_end;
+ MSL = BX_VGA_THIS s.CRTC.reg[0x09] & 0x1f;
+ *txHeight = (VDE+1)/(MSL+1);
+ *txWidth = BX_VGA_THIS s.CRTC.reg[1] + 1;
+ } else {
+ *txHeight = 0;
+ *txWidth = 0;
+ }
+}
+
+ Bit8u
+bx_vga_c::get_actl_palette_idx(Bit8u index)
+{
+ return BX_VGA_THIS s.attribute_ctrl.palette_reg[index];
+}
+
+ void
+bx_vga_c::dump_status(void)
+{
+ BX_INFO(("s.misc_output.color_emulation = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.color_emulation));
+ BX_INFO(("s.misc_output.enable_ram = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.enable_ram));
+ BX_INFO(("s.misc_output.clock_select = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.clock_select));
+ if (BX_VGA_THIS s.misc_output.clock_select == 0)
+ BX_INFO((" 25Mhz 640 horiz pixel clock"));
+ else
+ BX_INFO((" 28Mhz 720 horiz pixel clock"));
+ BX_INFO(("s.misc_output.select_high_bank = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.select_high_bank));
+ BX_INFO(("s.misc_output.horiz_sync_pol = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.horiz_sync_pol));
+ BX_INFO(("s.misc_output.vert_sync_pol = %u",
+ (unsigned) BX_VGA_THIS s.misc_output.vert_sync_pol));
+ switch ( (BX_VGA_THIS s.misc_output.vert_sync_pol << 1) |
+ BX_VGA_THIS s.misc_output.horiz_sync_pol ) {
+ case 0: BX_INFO((" (reserved")); break;
+ case 1: BX_INFO((" 400 lines")); break;
+ case 2: BX_INFO((" 350 lines")); break;
+ case 3: BX_INFO((" 480 lines")); break;
+ default: BX_INFO((" ???"));
+ }
+
+ BX_INFO(("s.graphics_ctrl.odd_even = %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.odd_even));
+ BX_INFO(("s.graphics_ctrl.chain_odd_even = %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.chain_odd_even));
+ BX_INFO(("s.graphics_ctrl.shift_reg = %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.shift_reg));
+ BX_INFO(("s.graphics_ctrl.graphics_alpha = %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.graphics_alpha));
+ BX_INFO(("s.graphics_ctrl.memory_mapping = %u",
+ (unsigned) BX_VGA_THIS s.graphics_ctrl.memory_mapping));
+ switch (BX_VGA_THIS s.graphics_ctrl.memory_mapping) {
+ case 0: BX_INFO((" A0000-BFFFF")); break;
+ case 1: BX_INFO((" A0000-AFFFF")); break;
+ case 2: BX_INFO((" B0000-B7FFF")); break;
+ case 3: BX_INFO((" B8000-BFFFF")); break;
+ default: BX_INFO((" ???"));
+ }
+
+ BX_INFO(("s.sequencer.extended_mem = %u",
+ (unsigned) BX_VGA_THIS s.sequencer.extended_mem));
+ BX_INFO(("s.sequencer.odd_even = %u (inverted)",
+ (unsigned) BX_VGA_THIS s.sequencer.odd_even));
+ BX_INFO(("s.sequencer.chain_four = %u",
+ (unsigned) BX_VGA_THIS s.sequencer.chain_four));
+
+ BX_INFO(("s.attribute_ctrl.video_enabled = %u",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.video_enabled));
+ BX_INFO(("s.attribute_ctrl.mode_ctrl.graphics_alpha = %u",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha));
+ BX_INFO(("s.attribute_ctrl.mode_ctrl.display_type = %u",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.display_type));
+ BX_INFO(("s.attribute_ctrl.mode_ctrl.internal_palette_size = %u",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.internal_palette_size));
+ BX_INFO(("s.attribute_ctrl.mode_ctrl.pixel_clock_select = %u",
+ (unsigned) BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select));
+}
+
+
+ void
+bx_vga_c::redraw_area(unsigned x0, unsigned y0, unsigned width,
+ unsigned height)
+{
+ unsigned xi, yi, x1, y1, xmax, ymax;
+
+ BX_VGA_THIS s.vga_mem_updated = 1;
+
+#if BX_SUPPORT_VBE
+ if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha || BX_VGA_THIS s.vbe_enabled) {
+#else
+ if (BX_VGA_THIS s.graphics_ctrl.graphics_alpha) {
+#endif
+ // graphics mode
+ x1 = x0 + width - 1;
+ y1 = y0 + height - 1;
+
+ xmax = old_iWidth;
+ ymax = old_iHeight;
+#if BX_SUPPORT_VBE
+ if (BX_VGA_THIS s.vbe_enabled) {
+ xmax = BX_VGA_THIS s.vbe_xres;
+ ymax = BX_VGA_THIS s.vbe_yres;
+ }
+#endif
+ for (yi=0; yi<ymax; yi+=Y_TILESIZE) {
+ for (xi=0; xi<xmax; xi+=X_TILESIZE) {
+ // is redraw rectangle outside x boundaries of this tile?
+ if (x1 < xi) continue;
+ if (x0 > (xi+X_TILESIZE-1)) continue;
+
+ // is redraw rectangle outside y boundaries of this tile?
+ if (y1 < yi) continue;
+ if (y0 > (yi+Y_TILESIZE-1)) continue;
+ unsigned xti = xi/X_TILESIZE;
+ unsigned yti = yi/Y_TILESIZE;
+ SET_TILE_UPDATED (xti, yti, 1);
+ }
+ }
+ }
+ else {
+ // text mode
+ memset(BX_VGA_THIS s.text_snapshot, 0,
+ sizeof(BX_VGA_THIS s.text_snapshot));
+ }
+}
+
+
+#if BX_SUPPORT_VBE
+ Bit8u BX_CPP_AttrRegparmN(1)
+bx_vga_c::vbe_mem_read(Bit32u addr)
+{
+ Bit32u offset;
+
+ if (addr >= VBE_DISPI_LFB_PHYSICAL_ADDRESS)
+ {
+ // LFB read
+ offset = addr - VBE_DISPI_LFB_PHYSICAL_ADDRESS;
+ }
+ else
+ {
+ // banked mode read
+ offset = BX_VGA_THIS s.vbe_bank*65536 + addr - 0xA0000;
+ }
+
+ // check for out of memory read
+ if (offset > VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)
+ return 0;
+
+ return (BX_VGA_THIS s.vbe_memory[offset]);
+}
+
+ void BX_CPP_AttrRegparmN(2)
+bx_vga_c::vbe_mem_write(Bit32u addr, Bit8u value)
+{
+ Bit32u offset;
+ unsigned x_tileno, y_tileno;
+
+ if (BX_VGA_THIS s.vbe_lfb_enabled)
+ {
+ if (addr >= VBE_DISPI_LFB_PHYSICAL_ADDRESS)
+ {
+ // LFB write
+ offset = addr - VBE_DISPI_LFB_PHYSICAL_ADDRESS;
+ }
+ else
+ {
+ // banked mode write while in LFB mode -> ignore
+ return;
+ }
+ }
+ else
+ {
+ if (addr < VBE_DISPI_LFB_PHYSICAL_ADDRESS)
+ {
+ // banked mode write
+ offset = (BX_VGA_THIS s.vbe_bank*65536) + (addr - 0xA0000);
+ }
+ else
+ {
+ // LFB write while in banked mode -> ignore
+ return;
+ }
+ }
+
+ // check for out of memory write
+ if (offset < VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)
+ {
+ BX_VGA_THIS s.vbe_memory[offset]=value;
+ }
+ else
+ {
+ // make sure we don't flood the logfile
+ static int count=0;
+ if (count<100)
+ {
+ count ++;
+ BX_INFO(("VBE_mem_write out of video memory write at %x",offset));
+ }
+ }
+
+ offset-=BX_VGA_THIS s.vbe_virtual_start;
+
+ // only update the UI when writing 'onscreen'
+ if (offset < BX_VGA_THIS s.vbe_visable_screen_size)
+ {
+ y_tileno = ((offset / BX_VGA_THIS s.vbe_bpp_multiplier) / BX_VGA_THIS s.vbe_virtual_xres) / Y_TILESIZE;
+ x_tileno = ((offset / BX_VGA_THIS s.vbe_bpp_multiplier) % BX_VGA_THIS s.vbe_virtual_xres) / X_TILESIZE;
+
+ if ((y_tileno < BX_NUM_Y_TILES) && (x_tileno < BX_NUM_X_TILES))
+ {
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ SET_TILE_UPDATED (x_tileno, y_tileno, 1);
+ }
+ }
+}
+
+ Bit32u
+bx_vga_c::vbe_read_handler(void *this_ptr, Bit32u address, unsigned io_len)
+{
+#if !BX_USE_VGA_SMF
+ bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
+
+ return( class_ptr->vbe_read(address, io_len) );
+}
+
+
+ Bit32u
+bx_vga_c::vbe_read(Bit32u address, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif // !BX_USE_VGA_SMF
+
+// BX_INFO(("VBE_read %x (len %x)", address, io_len));
+
+ if ((address==VBE_DISPI_IOPORT_INDEX) ||
+ (address==VBE_DISPI_IOPORT_INDEX_OLD))
+ {
+ // index register
+ return (Bit32u) BX_VGA_THIS s.vbe_curindex;
+ }
+ else
+ {
+ // data register read
+
+ switch (BX_VGA_THIS s.vbe_curindex)
+ {
+ case VBE_DISPI_INDEX_ID: // Display Interface ID check
+ {
+ return BX_VGA_THIS s.vbe_cur_dispi;
+ } break;
+
+ case VBE_DISPI_INDEX_XRES: // x resolution
+ {
+ return BX_VGA_THIS s.vbe_xres;
+ } break;
+
+ case VBE_DISPI_INDEX_YRES: // y resolution
+ {
+ return BX_VGA_THIS s.vbe_yres;
+ } break;
+
+ case VBE_DISPI_INDEX_BPP: // bpp
+ {
+ return BX_VGA_THIS s.vbe_bpp;
+ } break;
+
+ case VBE_DISPI_INDEX_ENABLE: // vbe enabled
+ {
+ return BX_VGA_THIS s.vbe_enabled;
+ } break;
+
+ case VBE_DISPI_INDEX_BANK: // current bank
+ {
+ return BX_VGA_THIS s.vbe_bank;
+ } break;
+
+ case VBE_DISPI_INDEX_X_OFFSET:
+ {
+ return BX_VGA_THIS s.vbe_offset_x;
+ } break;
+
+ case VBE_DISPI_INDEX_Y_OFFSET:
+ {
+ return BX_VGA_THIS s.vbe_offset_y;
+ } break;
+
+ case VBE_DISPI_INDEX_VIRT_WIDTH:
+ {
+ return BX_VGA_THIS s.vbe_virtual_xres;
+
+ } break;
+
+ case VBE_DISPI_INDEX_VIRT_HEIGHT:
+ {
+ return BX_VGA_THIS s.vbe_virtual_yres;
+ } break;
+
+
+ default:
+ {
+ BX_PANIC(("VBE unknown data read index 0x%x",BX_VGA_THIS s.vbe_curindex));
+ } break;
+ }
+ }
+ BX_PANIC(("VBE_read shouldn't reach this"));
+ return 0; /* keep compiler happy */
+}
+
+ void
+bx_vga_c::vbe_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len)
+{
+#if !BX_USE_VGA_SMF
+ bx_vga_c *class_ptr = (bx_vga_c *) this_ptr;
+
+ class_ptr->vbe_write(address, value, io_len);
+}
+
+ Bit32u
+bx_vga_c::vbe_write(Bit32u address, Bit32u value, unsigned io_len)
+{
+#else
+ UNUSED(this_ptr);
+#endif
+
+// BX_INFO(("VBE_write %x = %x (len %x)", address, value, io_len));
+
+ switch(address)
+ {
+ // index register
+ case VBE_DISPI_IOPORT_INDEX:
+ // legacy index register
+ case VBE_DISPI_IOPORT_INDEX_OLD:
+
+ BX_VGA_THIS s.vbe_curindex = (Bit16u) value;
+ break;
+
+ // data register
+ // FIXME: maybe do some 'sanity' checks on received data?
+ case VBE_DISPI_IOPORT_DATA:
+ // legacy data register
+ case VBE_DISPI_IOPORT_DATA_OLD:
+ switch (BX_VGA_THIS s.vbe_curindex)
+ {
+ case VBE_DISPI_INDEX_ID: // Display Interface ID check
+ {
+ if ( (value == VBE_DISPI_ID0) ||
+ (value == VBE_DISPI_ID1) ||
+ (value == VBE_DISPI_ID2) )
+ {
+ // allow backwards compatible with previous dispi bioses
+ BX_VGA_THIS s.vbe_cur_dispi=value;
+ }
+ else
+ {
+ BX_PANIC(("VBE unknown Display Interface %x",value));
+ }
+
+ // make sure we don't flood the logfile
+ static int count=0;
+ if (count < 100)
+ {
+ count++;
+ BX_INFO(("VBE known Display Interface %x",value));
+ }
+ } break;
+
+ case VBE_DISPI_INDEX_XRES: // set xres
+ {
+ // check that we don't set xres during vbe enabled
+ if (!BX_VGA_THIS s.vbe_enabled)
+ {
+ // check for within max xres range
+ if (value <= VBE_DISPI_MAX_XRES)
+ {
+ BX_VGA_THIS s.vbe_xres=(Bit16u) value;
+ BX_INFO(("VBE set xres (%d)",value));
+ }
+ else
+ {
+ BX_INFO(("VBE set xres more then max xres (%d)",value));
+ }
+ }
+ else
+ {
+ BX_INFO(("VBE set xres during vbe enabled!"));
+ }
+ } break;
+
+ case VBE_DISPI_INDEX_YRES: // set yres
+ {
+ // check that we don't set yres during vbe enabled
+ if (!BX_VGA_THIS s.vbe_enabled)
+ {
+ // check for within max yres range
+ if (value <= VBE_DISPI_MAX_YRES)
+ {
+ BX_VGA_THIS s.vbe_yres=(Bit16u) value;
+ BX_INFO(("VBE set yres (%d)",value));
+ }
+ else
+ {
+ BX_INFO(("VBE set yres more then max yres (%d)",value));
+ }
+ }
+ else
+ {
+ BX_INFO(("VBE set yres during vbe enabled!"));
+ }
+ } break;
+
+ case VBE_DISPI_INDEX_BPP: // set bpp
+ {
+ // check that we don't set bpp during vbe enabled
+ if (!BX_VGA_THIS s.vbe_enabled)
+ {
+ // for backward compatiblity
+ if (value == 0) value = VBE_DISPI_BPP_8;
+ // check for correct bpp range
+ if ((value == VBE_DISPI_BPP_4) || (value == VBE_DISPI_BPP_8) || (value == VBE_DISPI_BPP_15) ||
+ (value == VBE_DISPI_BPP_16) || (value == VBE_DISPI_BPP_24) || (value == VBE_DISPI_BPP_32))
+ {
+ BX_VGA_THIS s.vbe_bpp=(Bit16u) value;
+ BX_INFO(("VBE set bpp (%d)",value));
+ }
+ else
+ {
+ BX_INFO(("VBE set bpp with unknown bpp (%d)",value));
+ }
+ }
+ else
+ {
+ BX_INFO(("VBE set bpp during vbe enabled!"));
+ }
+ } break;
+
+ case VBE_DISPI_INDEX_BANK: // set bank
+ {
+ value=value & 0xff ; // FIXME lobyte = vbe bank A?
+
+ // check for max bank nr
+ if (value < (VBE_DISPI_TOTAL_VIDEO_MEMORY_KB /64))
+ {
+ if (!BX_VGA_THIS s.vbe_lfb_enabled)
+ {
+ BX_DEBUG(("VBE set bank to %d", value));
+ BX_VGA_THIS s.vbe_bank=value;
+ }
+ else
+ {
+ BX_ERROR(("VBE set bank in LFB mode ignored"));
+ }
+ }
+ else
+ {
+ BX_INFO(("VBE set invalid bank (%d)",value));
+ }
+ } break;
+
+ case VBE_DISPI_INDEX_ENABLE: // enable video
+ {
+ if (value & VBE_DISPI_ENABLED)
+ {
+ unsigned depth=0;
+
+ // setup virtual resolution to be the same as current reso
+ BX_VGA_THIS s.vbe_virtual_yres=BX_VGA_THIS s.vbe_yres;
+ BX_VGA_THIS s.vbe_virtual_xres=BX_VGA_THIS s.vbe_xres;
+
+ // reset offset
+ BX_VGA_THIS s.vbe_offset_x=0;
+ BX_VGA_THIS s.vbe_offset_y=0;
+ BX_VGA_THIS s.vbe_virtual_start=0;
+
+ switch((BX_VGA_THIS s.vbe_bpp))
+ {
+ // Default pixel sizes
+ case VBE_DISPI_BPP_8:
+ BX_VGA_THIS s.vbe_bpp_multiplier = 1;
+ BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres;
+ BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres));
+ depth=8;
+ break;
+
+ case VBE_DISPI_BPP_4:
+ BX_VGA_THIS s.vbe_bpp_multiplier = 1;
+ BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres;
+ BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres));
+ depth=4;
+ break;
+
+ case VBE_DISPI_BPP_15:
+ BX_VGA_THIS s.vbe_bpp_multiplier = 2;
+ BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres * 2;
+ BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) * 2;
+ depth=15;
+ break;
+
+ case VBE_DISPI_BPP_16:
+ BX_VGA_THIS s.vbe_bpp_multiplier = 2;
+ BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres * 2;
+ BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) * 2;
+ depth=16;
+ break;
+
+ case VBE_DISPI_BPP_24:
+ BX_VGA_THIS s.vbe_bpp_multiplier = 3;
+ BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres * 3;
+ BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) * 3;
+ depth=24;
+ break;
+
+ case VBE_DISPI_BPP_32:
+ BX_VGA_THIS s.vbe_bpp_multiplier = 4;
+ BX_VGA_THIS s.vbe_line_byte_width = BX_VGA_THIS s.vbe_virtual_xres << 2;
+ BX_VGA_THIS s.vbe_visable_screen_size = ((BX_VGA_THIS s.vbe_xres) * (BX_VGA_THIS s.vbe_yres)) << 2;
+ depth=32;
+ break;
+ }
+
+ BX_INFO(("VBE enabling x %d, y %d, bpp %d, %u bytes visible", BX_VGA_THIS s.vbe_xres, BX_VGA_THIS s.vbe_yres, BX_VGA_THIS s.vbe_bpp, BX_VGA_THIS s.vbe_visable_screen_size));
+
+ if (depth > 4)
+ {
+ BX_VGA_THIS s.vbe_lfb_enabled=(bx_bool)(value & VBE_DISPI_LFB_ENABLED);
+ if ((value & VBE_DISPI_NOCLEARMEM) == 0)
+ {
+ memset(BX_VGA_THIS s.vbe_memory, 0, BX_VGA_THIS s.vbe_visable_screen_size);
+ }
+ bx_gui->dimension_update(BX_VGA_THIS s.vbe_xres, BX_VGA_THIS s.vbe_yres, 0, 0, depth);
+ old_BPP = depth;
+ // some test applications expect these standard VGA settings
+ BX_VGA_THIS s.CRTC.reg[9] = 0x00;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.graphics_alpha = 1;
+ BX_VGA_THIS s.graphics_ctrl.memory_mapping = 1;
+ BX_VGA_THIS s.CRTC.reg[1] = (BX_VGA_THIS s.vbe_xres / 8) - 1;
+ BX_VGA_THIS s.CRTC.reg[19] = BX_VGA_THIS s.vbe_xres >> 2;
+ BX_VGA_THIS s.attribute_ctrl.mode_ctrl.pixel_clock_select = 1;
+ BX_VGA_THIS s.CRTC.reg[18] = (BX_VGA_THIS s.vbe_yres - 1) & 0xff;
+ BX_VGA_THIS s.CRTC.reg[7] &= ~0x42;
+ if ((BX_VGA_THIS s.vbe_yres - 1) & 0x0100) {
+ BX_VGA_THIS s.CRTC.reg[7] |= 0x02;
+ }
+ if ((BX_VGA_THIS s.vbe_yres - 1) & 0x0200) {
+ BX_VGA_THIS s.CRTC.reg[7] |= 0x40;
+ }
+ }
+ }
+ else
+ {
+ if (BX_VGA_THIS s.vbe_enabled) BX_INFO(("VBE disabling"));
+ BX_VGA_THIS s.vbe_lfb_enabled=0;
+ }
+ BX_VGA_THIS s.vbe_enabled=(bx_bool)(value & VBE_DISPI_ENABLED);
+ } break;
+
+ case VBE_DISPI_INDEX_X_OFFSET:
+ {
+ // BX_INFO(("VBE offset x %x",value));
+ BX_VGA_THIS s.vbe_offset_x=(Bit16u)value;
+
+ BX_VGA_THIS s.vbe_virtual_start = ((BX_VGA_THIS s.vbe_offset_y) * (BX_VGA_THIS s.vbe_line_byte_width)) +
+ ((BX_VGA_THIS s.vbe_offset_x) * (BX_VGA_THIS s.vbe_bpp_multiplier));
+
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ for (unsigned xti = 0; xti < BX_NUM_X_TILES; xti++) {
+ for (unsigned yti = 0; yti < BX_NUM_Y_TILES; yti++) {
+ SET_TILE_UPDATED (xti, yti, 1);
+ }
+ }
+ } break;
+
+ case VBE_DISPI_INDEX_Y_OFFSET:
+ {
+ // BX_INFO(("VBE offset y %x",value));
+ BX_VGA_THIS s.vbe_offset_y=(Bit16u)value;
+ BX_VGA_THIS s.vbe_virtual_start = ((BX_VGA_THIS s.vbe_offset_y) * (BX_VGA_THIS s.vbe_line_byte_width)) +
+ ((BX_VGA_THIS s.vbe_offset_x) * (BX_VGA_THIS s.vbe_bpp_multiplier));
+
+ BX_VGA_THIS s.vga_mem_updated = 1;
+ for (unsigned xti = 0; xti < BX_NUM_X_TILES; xti++) {
+ for (unsigned yti = 0; yti < BX_NUM_Y_TILES; yti++) {
+ SET_TILE_UPDATED (xti, yti, 1);
+ }
+ }
+ } break;
+
+ case VBE_DISPI_INDEX_VIRT_WIDTH:
+ {
+ BX_INFO(("VBE requested virtual width %d",value));
+
+ // calculate virtual width & height dimensions
+ // req:
+ // virt_width > xres
+ // virt_height >=yres
+ // virt_width*virt_height < MAX_VIDEO_MEMORY
+
+ // basicly 2 situations
+
+ // situation 1:
+ // MAX_VIDEO_MEMORY / virt_width >= yres
+ // adjust result height
+ // else
+ // adjust result width based upon virt_height=yres
+ Bit16u new_width=value;
+ Bit16u new_height=(sizeof(BX_VGA_THIS s.vbe_memory) / BX_VGA_THIS s.vbe_bpp_multiplier) / new_width;
+ if (new_height >=BX_VGA_THIS s.vbe_yres)
+ {
+ // we have a decent virtual width & new_height
+ BX_INFO(("VBE decent virtual height %d",new_height));
+ }
+ else
+ {
+ // no decent virtual height: adjust width & height
+ new_height=BX_VGA_THIS s.vbe_yres;
+ new_width=(sizeof(BX_VGA_THIS s.vbe_memory) / BX_VGA_THIS s.vbe_bpp_multiplier) / new_height;
+
+ BX_INFO(("VBE recalc virtual width %d height %d",new_width, new_height));
+ }
+
+ BX_VGA_THIS s.vbe_virtual_xres=new_width;
+ BX_VGA_THIS s.vbe_virtual_yres=new_height;
+ BX_VGA_THIS s.vbe_visable_screen_size = (new_width * (BX_VGA_THIS s.vbe_yres)) * BX_VGA_THIS s.vbe_bpp_multiplier;
+
+ } break;
+ /*
+ case VBE_DISPI_INDEX_VIRT_HEIGHT:
+ {
+ BX_INFO(("VBE virtual height %x",value));
+
+ } break;
+ */
+ default:
+ {
+ BX_PANIC(("VBE unknown data write index 0x%x",BX_VGA_THIS s.vbe_curindex));
+ } break;
+ }
+ break;
+
+ } // end switch address
+}
+
+#endif
diff --git a/tools/ioemu/iodev/vga.h b/tools/ioemu/iodev/vga.h
new file mode 100644
index 0000000000..cafb60cd6b
--- /dev/null
+++ b/tools/ioemu/iodev/vga.h
@@ -0,0 +1,300 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: vga.h,v 1.36 2003/12/31 10:33:27 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+#if BX_SUPPORT_VBE
+ #define VBE_DISPI_TOTAL_VIDEO_MEMORY_MB 4
+
+ #define VBE_DISPI_BANK_ADDRESS 0xA0000
+ #define VBE_DISPI_BANK_SIZE_KB 64
+
+ #define VBE_DISPI_MAX_XRES 1024
+ #define VBE_DISPI_MAX_YRES 768
+
+ #define VBE_DISPI_IOPORT_INDEX 0x01CE
+ #define VBE_DISPI_IOPORT_DATA 0x01CF
+
+ #define VBE_DISPI_IOPORT_INDEX_OLD 0xFF80
+ #define VBE_DISPI_IOPORT_DATA_OLD 0xFF81
+
+ #define VBE_DISPI_INDEX_ID 0x0
+ #define VBE_DISPI_INDEX_XRES 0x1
+ #define VBE_DISPI_INDEX_YRES 0x2
+ #define VBE_DISPI_INDEX_BPP 0x3
+ #define VBE_DISPI_INDEX_ENABLE 0x4
+ #define VBE_DISPI_INDEX_BANK 0x5
+ #define VBE_DISPI_INDEX_VIRT_WIDTH 0x6
+ #define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7
+ #define VBE_DISPI_INDEX_X_OFFSET 0x8
+ #define VBE_DISPI_INDEX_Y_OFFSET 0x9
+
+ #define VBE_DISPI_ID0 0xB0C0
+ #define VBE_DISPI_ID1 0xB0C1
+ #define VBE_DISPI_ID2 0xB0C2
+
+ #define VBE_DISPI_BPP_4 0x04
+ #define VBE_DISPI_BPP_8 0x08
+ #define VBE_DISPI_BPP_15 0x0F
+ #define VBE_DISPI_BPP_16 0x10
+ #define VBE_DISPI_BPP_24 0x18
+ #define VBE_DISPI_BPP_32 0x20
+
+ #define VBE_DISPI_DISABLED 0x00
+ #define VBE_DISPI_ENABLED 0x01
+ #define VBE_DISPI_NOCLEARMEM 0x80
+ #define VBE_DISPI_LFB_ENABLED 0x40
+
+ #define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000
+
+
+#define VBE_DISPI_TOTAL_VIDEO_MEMORY_KB (VBE_DISPI_TOTAL_VIDEO_MEMORY_MB * 1024)
+#define VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES (VBE_DISPI_TOTAL_VIDEO_MEMORY_KB * 1024)
+
+#define BX_MAX_XRES VBE_DISPI_MAX_XRES
+#define BX_MAX_YRES VBE_DISPI_MAX_YRES
+
+#else
+
+#define BX_MAX_XRES 800
+#define BX_MAX_YRES 600
+
+#endif //BX_SUPPORT_VBE
+
+#define X_TILESIZE 16
+#define Y_TILESIZE 24
+#define BX_NUM_X_TILES (BX_MAX_XRES /X_TILESIZE)
+#define BX_NUM_Y_TILES (BX_MAX_YRES /Y_TILESIZE)
+
+// Support varying number of rows of text. This used to
+// be limited to only 25 lines.
+#define BX_MAX_TEXT_LINES 100
+
+#if BX_USE_VGA_SMF
+# define BX_VGA_SMF static
+# define BX_VGA_THIS theVga->
+#else
+# define BX_VGA_SMF
+# define BX_VGA_THIS this->
+#endif
+
+
+class bx_vga_c : public bx_vga_stub_c {
+public:
+
+ bx_vga_c(void);
+ ~bx_vga_c(void);
+ virtual void init(void);
+ virtual void bios_init(void);
+ virtual void reset(unsigned type);
+ virtual Bit8u mem_read(Bit32u addr);
+ // Note: either leave value of type Bit8u, or mask it when
+ // used to 8 bits, in memory.cc
+ virtual void mem_write(Bit32u addr, Bit8u value);
+ virtual void trigger_timer(void *this_ptr);
+
+#if BX_SUPPORT_VBE
+ BX_VGA_SMF Bit8u vbe_mem_read(Bit32u addr) BX_CPP_AttrRegparmN(1);
+ BX_VGA_SMF void vbe_mem_write(Bit32u addr, Bit8u value) BX_CPP_AttrRegparmN(2);
+#endif
+
+ virtual void redraw_area(unsigned x0, unsigned y0,
+ unsigned width, unsigned height);
+
+ virtual void set_update_interval (unsigned interval);
+ virtual void get_text_snapshot(Bit8u **text_snapshot, unsigned *txHeight,
+ unsigned *txWidth);
+ virtual Bit8u get_actl_palette_idx(Bit8u index);
+
+private:
+
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+ static void write_handler_no_log(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+
+#if BX_SUPPORT_VBE
+ static Bit32u vbe_read_handler(void *this_ptr, Bit32u address, unsigned io_len);
+ static void vbe_write_handler(void *this_ptr, Bit32u address, Bit32u value, unsigned io_len);
+#endif
+
+ struct {
+ struct {
+ bx_bool color_emulation; // 1=color emulation, base address = 3Dx
+ // 0=mono emulation, base address = 3Bx
+ bx_bool enable_ram; // enable CPU access to video memory if set
+ Bit8u clock_select; // 0=25Mhz 1=28Mhz
+ bx_bool select_high_bank; // when in odd/even modes, select
+ // high 64k bank if set
+ bx_bool horiz_sync_pol; // bit6: negative if set
+ bx_bool vert_sync_pol; // bit7: negative if set
+ // bit7,bit6 represent number of lines on display:
+ // 0 = reserved
+ // 1 = 400 lines
+ // 2 = 350 lines
+ // 3 - 480 lines
+ } misc_output;
+
+ struct {
+ Bit8u address;
+ Bit8u reg[0x19];
+ } CRTC;
+
+ struct {
+ bx_bool flip_flop; /* 0 = address, 1 = data-write */
+ unsigned address; /* register number */
+ bx_bool video_enabled;
+ Bit8u palette_reg[16];
+ Bit8u overscan_color;
+ Bit8u color_plane_enable;
+ Bit8u horiz_pel_panning;
+ Bit8u color_select;
+ struct {
+ bx_bool graphics_alpha;
+ bx_bool display_type;
+ bx_bool enable_line_graphics;
+ bx_bool blink_intensity;
+ bx_bool pixel_panning_compat;
+ bx_bool pixel_clock_select;
+ bx_bool internal_palette_size;
+ } mode_ctrl;
+ } attribute_ctrl;
+
+ struct {
+ Bit8u write_data_register;
+ Bit8u write_data_cycle; /* 0, 1, 2 */
+ Bit8u read_data_register;
+ Bit8u read_data_cycle; /* 0, 1, 2 */
+ Bit8u dac_state;
+ struct {
+ Bit8u red;
+ Bit8u green;
+ Bit8u blue;
+ } data[256];
+ Bit8u mask;
+ } pel;
+
+
+ struct {
+ Bit8u index;
+ Bit8u set_reset;
+ Bit8u enable_set_reset;
+ Bit8u color_compare;
+ Bit8u data_rotate;
+ Bit8u raster_op;
+ Bit8u read_map_select;
+ Bit8u write_mode;
+ bx_bool read_mode;
+ bx_bool odd_even;
+ bx_bool chain_odd_even;
+ Bit8u shift_reg;
+ bx_bool graphics_alpha;
+ Bit8u memory_mapping; /* 0 = use A0000-BFFFF
+ * 1 = use A0000-AFFFF EGA/VGA graphics modes
+ * 2 = use B0000-B7FFF Monochrome modes
+ * 3 = use B8000-BFFFF CGA modes
+ */
+ Bit8u color_dont_care;
+ Bit8u bitmask;
+ Bit8u latch[4];
+ } graphics_ctrl;
+
+ struct {
+ Bit8u index;
+ Bit8u map_mask;
+ bx_bool map_mask_bit[4];
+ bx_bool reset1;
+ bx_bool reset2;
+ Bit8u reg1;
+ Bit8u char_map_select;
+ bx_bool extended_mem;
+ bx_bool odd_even;
+ bx_bool chain_four;
+ } sequencer;
+
+ bx_bool vga_mem_updated;
+ unsigned x_tilesize;
+ unsigned y_tilesize;
+ unsigned line_offset;
+ unsigned line_compare;
+ unsigned vertical_display_end;
+ bx_bool vga_tile_updated[BX_NUM_X_TILES][BX_NUM_Y_TILES];
+ Bit8u vga_memory[256 * 1024];
+ Bit8u text_snapshot[32 * 1024]; // current text snapshot
+ Bit8u rgb[3 * 256];
+ Bit8u tile[X_TILESIZE * Y_TILESIZE * 4]; /**< Currently allocates the tile as large as needed. */
+ Bit16u charmap_address;
+ bx_bool x_dotclockdiv2;
+ bx_bool y_doublescan;
+
+#if BX_SUPPORT_VBE
+ Bit8u vbe_memory[VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES];
+ Bit16u vbe_cur_dispi;
+ Bit16u vbe_xres;
+ Bit16u vbe_yres;
+ Bit16u vbe_bpp;
+ Bit16u vbe_bank;
+ bx_bool vbe_enabled;
+ Bit16u vbe_curindex;
+ Bit32u vbe_visable_screen_size; /**< in bytes */
+ Bit16u vbe_offset_x; /**< Virtual screen x start (in pixels) */
+ Bit16u vbe_offset_y; /**< Virtual screen y start (in pixels) */
+ Bit16u vbe_virtual_xres;
+ Bit16u vbe_virtual_yres;
+ Bit16u vbe_line_byte_width; /**< For dealing with bpp>8, this is they width of a line in bytes. */
+ Bit32u vbe_virtual_start; /**< For dealing with bpp>8, this is where the virtual screen starts. */
+ Bit8u vbe_bpp_multiplier; /**< We have to save this b/c sometimes we need to recalculate stuff with it. */
+ bx_bool vbe_lfb_enabled;
+#endif
+ } s; // state information
+
+
+#if !BX_USE_VGA_SMF
+ Bit32u read(Bit32u address, unsigned io_len);
+ void write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log);
+#else
+ void write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log);
+#endif
+
+#if BX_SUPPORT_VBE
+
+#if !BX_USE_VGA_SMF
+ Bit32u vbe_read(Bit32u address, unsigned io_len);
+ void vbe_write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log);
+#else
+ void vbe_write(Bit32u address, Bit32u value, unsigned io_len, bx_bool no_log);
+#endif
+#endif
+
+ int timer_id;
+
+ public:
+ static void timer_handler(void *);
+ BX_VGA_SMF void timer(void);
+
+ private:
+ BX_VGA_SMF void update(void);
+ BX_VGA_SMF void dump_status(void);
+ BX_VGA_SMF void determine_screen_dimensions(unsigned *piHeight,
+ unsigned *piWidth);
+ };
diff --git a/tools/ioemu/iodev/virt_timer.cc b/tools/ioemu/iodev/virt_timer.cc
new file mode 100644
index 0000000000..eb108a025b
--- /dev/null
+++ b/tools/ioemu/iodev/virt_timer.cc
@@ -0,0 +1,552 @@
+////////////////////////////////////////////////////////////////////////
+// $Id: virt_timer.cc,v 1.19.2.1 2004/02/06 22:14:36 danielg4 Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+/////////////////////////////////////////////////////////////////////////
+//
+//Realtime Algorithm (with gettimeofday)
+// HAVE:
+// Real number of usec.
+// Emulated number of usec.
+// WANT:
+// Number of ticks to use.
+// Number of emulated usec to wait until next try.
+//
+// ticks=number of ticks needed to match total real usec.
+// if(desired ticks > max ticks for elapsed real time)
+// ticks = max ticks for elapsed real time.
+// if(desired ticks > max ticks for elapsed emulated usec)
+// ticks = max ticks for emulated usec.
+// next wait ticks = number of ticks until next event.
+// next wait real usec = (current ticks + next wait ticks) * usec per ticks
+// next wait emulated usec = next wait real usec * emulated usec / real usec
+// if(next wait emulated usec < minimum emulated usec for next wait ticks)
+// next wait emulated usec = minimum emulated usec for next wait ticks.
+// if(next wait emulated usec > max emulated usec wait)
+// next wait emulated usec = max emulated usec wait.
+//
+// How to calculate elapsed real time:
+// store an unused time value whenever no ticks are used in a given time.
+// add this to the current elapsed time.
+// How to calculate elapsed emulated time:
+// same as above.
+// Above can be done by not updating last_usec and last_sec.
+//
+// How to calculate emulated usec/real usec:
+// Each time there are actual ticks:
+// Alpha_product(old emulated usec, emulated usec);
+// Alpha_product(old real usec, real usec);
+// Divide resulting values.
+//
+/////////////////////////////////////////////////////////////////////////
+
+#include "bochs.h"
+
+#define BX_USE_VIRTUAL_TIMERS 1
+#define BX_VIRTUAL_TIMERS_REALTIME 1
+
+//Important constant #defines:
+#define USEC_PER_SECOND (1000000)
+
+
+// define a macro to convert floating point numbers into 64-bit integers.
+// In MSVC++ you can convert a 64-bit float into a 64-bit signed integer,
+// but it will not convert a 64-bit float into a 64-bit unsigned integer.
+// This macro works around that.
+#define F2I(x) ((Bit64u)(Bit64s) (x))
+#define I2F(x) ((double)(Bit64s) (x))
+
+//CONFIGURATION #defines:
+
+
+//MAINLINE Configuration (For realtime PIT):
+
+//How much faster than real time we can go:
+#define MAX_MULT (1.25)
+
+//Minimum number of emulated useconds per second.
+// Now calculated using BX_MIN_IPS, the minimum number of
+// instructions per second.
+#define MIN_USEC_PER_SECOND (((((Bit64u)USEC_PER_SECOND)*((Bit64u)BX_MIN_IPS))/((Bit64u)(bx_options.Oips->get())))+(Bit64u)1)
+
+
+//DEBUG configuration:
+
+//Debug with printf options.
+#define DEBUG_REALTIME_WITH_PRINTF 0
+
+//Use to test execution at multiples of real time.
+#define TIME_DIVIDER (1)
+#define TIME_MULTIPLIER (1)
+#define TIME_HEADSTART (0)
+
+
+#define GET_VIRT_REALTIME64_USEC() (((bx_get_realtime64_usec()*(Bit64u)TIME_MULTIPLIER/(Bit64u)TIME_DIVIDER)))
+//Set up Logging.
+#define LOG_THIS bx_virt_timer.
+
+//A single instance.
+bx_virt_timer_c bx_virt_timer;
+
+
+//Generic MAX and MIN Functions
+#define BX_MAX(a,b) ( ((a)>(b))?(a):(b) )
+#define BX_MIN(a,b) ( ((a)>(b))?(b):(a) )
+
+
+//USEC_ALPHA is multiplier for the past.
+//USEC_ALPHA_B is 1-USEC_ALPHA, or multiplier for the present.
+#define USEC_ALPHA ((double)(.8))
+#define USEC_ALPHA_B ((double)(((double)1)-USEC_ALPHA))
+#define USEC_ALPHA2 ((double)(.5))
+#define USEC_ALPHA2_B ((double)(((double)1)-USEC_ALPHA2))
+#define ALPHA_LOWER(old,new) ((Bit64u)((old<new)?((USEC_ALPHA*(I2F(old)))+(USEC_ALPHA_B*(I2F(new)))):((USEC_ALPHA2*(I2F(old)))+(USEC_ALPHA2_B*(I2F(new))))))
+
+
+//Conversion between emulated useconds and optionally realtime ticks.
+#define TICKS_TO_USEC(a) ( ((a)*usec_per_second)/ticks_per_second )
+#define USEC_TO_TICKS(a) ( ((a)*ticks_per_second)/usec_per_second )
+
+bx_virt_timer_c::bx_virt_timer_c( void )
+{
+ put("VTIMER");
+ settype(VTIMERLOG);
+
+ numTimers = 0;
+ current_timers_time = 0;
+ timers_next_event_time = BX_MAX_VIRTUAL_TIME;
+ last_sequential_time = 0;
+ in_timer_handler = 0;
+ virtual_next_event_time = BX_MAX_VIRTUAL_TIME;
+ current_virtual_time = 0;
+
+ use_virtual_timers = BX_USE_VIRTUAL_TIMERS;
+ init_done = 0;
+}
+
+bx_virt_timer_c::~bx_virt_timer_c( void )
+{
+}
+
+
+
+const Bit64u bx_virt_timer_c::NullTimerInterval = BX_MAX_VIRTUAL_TIME;
+
+void
+bx_virt_timer_c::nullTimer(void* this_ptr) {
+ UNUSED(this_ptr);
+}
+
+void
+bx_virt_timer_c::periodic(Bit64u time_passed) {
+ //Assert that we haven't skipped any events.
+ BX_ASSERT (time_passed <= timers_next_event_time);
+ BX_ASSERT(!in_timer_handler);
+
+ //Update time variables.
+ timers_next_event_time -= time_passed;
+ current_timers_time += time_passed;
+
+ //If no events are occurring, just pass the time and we're done.
+ if( time_passed < timers_next_event_time ) {
+ return;
+ }
+ //Starting timer handler calls.
+ in_timer_handler = 1;
+ //Otherwise, cause any events to occur that should.
+ unsigned i;
+ for(i=0;i<numTimers;i++) {
+ if( timer[i].inUse && timer[i].active ) {
+ //Assert that we haven't skipped any timers.
+ BX_ASSERT(current_timers_time <= timer[i].timeToFire);
+ if(timer[i].timeToFire == current_timers_time) {
+ if(timer[i].continuous) {
+ timer[i].timeToFire+=timer[i].period;
+ } else {
+ timer[i].active = 0;
+ }
+ //This function MUST return, or the timer mechanism
+ // will be broken.
+ timer[i].funct(timer[i].this_ptr);
+ }
+ }
+ }
+ //Finished timer handler calls.
+ in_timer_handler = 0;
+ //Use a second FOR loop so that a timer function call can
+ // change the behavior of another timer.
+ //timers_next_event_time normally contains a cycle count, not a cycle time.
+ // here we use it as a temporary variable that IS a cycle time,
+ // but then convert it back to a cycle count afterwards.
+ timers_next_event_time = current_timers_time + BX_MAX_VIRTUAL_TIME;
+ for(i=0;i<numTimers;i++) {
+ if( timer[i].inUse && timer[i].active && ((timer[i].timeToFire)<timers_next_event_time) ) {
+ timers_next_event_time = timer[i].timeToFire;
+ }
+ }
+ timers_next_event_time-=current_timers_time;
+ next_event_time_update();
+ //FIXME
+}
+
+
+//Get the current virtual time.
+// This may return the same value on subsequent calls.
+Bit64u
+bx_virt_timer_c::time_usec(void) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.time_usec();
+ }
+
+ //Update the time here only if we're not in a timer handler.
+ //If we're in a timer handler we're up-to-date, and otherwise
+ // this prevents call stack loops.
+ if(!in_timer_handler) {
+ timer_handler();
+ }
+
+ return current_timers_time;
+}
+
+//Get the current virtual time.
+// This will return a monotonically increasing value.
+// MUST NOT be called from within a timer interrupt.
+Bit64u
+bx_virt_timer_c::time_usec_sequential(void) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.time_usec_sequential();
+ }
+
+ //Can't prevent call stack loops here, so this
+ // MUST NOT be called from within a timer handler.
+ BX_ASSERT(timers_next_event_time>0);
+ BX_ASSERT(!in_timer_handler);
+
+ if(last_sequential_time >= current_timers_time) {
+ periodic(1);
+ last_sequential_time = current_timers_time;
+ }
+ return current_timers_time;
+}
+
+
+
+//Register a timer handler to go off after a given interval.
+//Register a timer handler to go off with a periodic interval.
+int
+bx_virt_timer_c::register_timer( void *this_ptr, bx_timer_handler_t handler,
+ Bit32u useconds,
+ bx_bool continuous, bx_bool active,
+ const char *id) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.register_timer(this_ptr, handler, useconds,
+ continuous, active, id);
+ }
+
+ //We don't like starting with a zero period timer.
+ BX_ASSERT((!active) || (useconds>0));
+
+ //Search for an unused timer.
+ unsigned int i;
+ for (i=0; i < numTimers; i++) {
+ if (timer[i].inUse == 0 || i==numTimers)
+ break;
+ }
+ // If we didn't find a free slot, increment the bound, numTimers.
+ if (i==numTimers)
+ numTimers++; // One new timer installed.
+ BX_ASSERT(numTimers<BX_MAX_VIRTUAL_TIMERS);
+
+ timer[i].inUse = 1;
+ timer[i].period = useconds;
+ timer[i].timeToFire = current_timers_time + (Bit64u)useconds;
+ timer[i].active = active;
+ timer[i].continuous = continuous;
+ timer[i].funct = handler;
+ timer[i].this_ptr = this_ptr;
+ strncpy(timer[i].id, id, BxMaxTimerIDLen);
+ timer[i].id[BxMaxTimerIDLen-1]=0; //I like null terminated strings.
+
+ if(useconds < timers_next_event_time) {
+ timers_next_event_time = useconds;
+ next_event_time_update();
+ //FIXME
+ }
+ return i;
+}
+
+//unregister a previously registered timer.
+unsigned
+bx_virt_timer_c::unregisterTimer(int timerID) {
+ if(!use_virtual_timers) {
+ return bx_pc_system.unregisterTimer(timerID);
+ }
+
+ BX_ASSERT(timerID >= 0);
+ BX_ASSERT(timerID < BX_MAX_VIRTUAL_TIMERS);
+
+ if (timer[timerID].active) {
+ BX_PANIC(("unregisterTimer: timer '%s' is still active!", timer[timerID].id));
+ return(0); // Fail.
+ }
+
+
+ //No need to prevent doing this to unused timers.
+ timer[timerID].inUse = 0;
+ return(1);
+}
+
+void
+bx_virt_timer_c::start_timers(void) {
+ if(!use_virtual_timers) {
+ bx_pc_system.start_timers();
+ return;
+ }
+ //FIXME
+}
+
+//activate a deactivated but registered timer.
+void
+bx_virt_timer_c::activate_timer( unsigned timer_index, Bit32u useconds,
+ bx_bool continuous ) {
+ if(!use_virtual_timers) {
+ bx_pc_system.activate_timer(timer_index, useconds, continuous);
+ return;
+ }
+
+ BX_ASSERT(timer_index >= 0);
+ BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS);
+
+ BX_ASSERT(timer[timer_index].inUse);
+ BX_ASSERT(useconds>0);
+
+ timer[timer_index].period=useconds;
+ timer[timer_index].timeToFire = current_timers_time + (Bit64u)useconds;
+ timer[timer_index].active=1;
+ timer[timer_index].continuous=continuous;
+
+ if(useconds < timers_next_event_time) {
+ timers_next_event_time = useconds;
+ next_event_time_update();
+ //FIXME
+ }
+}
+
+//deactivate (but don't unregister) a currently registered timer.
+void
+bx_virt_timer_c::deactivate_timer( unsigned timer_index ) {
+ if(!use_virtual_timers) {
+ bx_pc_system.deactivate_timer(timer_index);
+ return;
+ }
+
+ BX_ASSERT(timer_index >= 0);
+ BX_ASSERT(timer_index < BX_MAX_VIRTUAL_TIMERS);
+
+ //No need to prevent doing this to unused/inactive timers.
+ timer[timer_index].active = 0;
+}
+
+void
+bx_virt_timer_c::advance_virtual_time(Bit64u time_passed) {
+ BX_ASSERT(time_passed <= virtual_next_event_time);
+
+ current_virtual_time += time_passed;
+ virtual_next_event_time -= time_passed;
+
+ if(current_virtual_time > current_timers_time) {
+ periodic(current_virtual_time - current_timers_time);
+ }
+}
+
+//Called when next_event_time changes.
+void
+bx_virt_timer_c::next_event_time_update(void) {
+ virtual_next_event_time = timers_next_event_time + current_timers_time - current_virtual_time;
+ if(init_done) {
+ bx_pc_system.deactivate_timer(system_timer_id);
+ BX_ASSERT(virtual_next_event_time);
+ bx_pc_system.activate_timer(system_timer_id,
+ (Bit32u)BX_MIN(0x7FFFFFFF,BX_MAX(1,TICKS_TO_USEC(virtual_next_event_time))),
+ 0);
+ }
+}
+
+void
+bx_virt_timer_c::init(void) {
+
+ if ( (bx_options.clock.Osync->get ()!=BX_CLOCK_SYNC_REALTIME)
+ && (bx_options.clock.Osync->get ()!=BX_CLOCK_SYNC_BOTH) )
+ virtual_timers_realtime = 0;
+ else
+ virtual_timers_realtime = 1;
+
+ if (virtual_timers_realtime) {
+ BX_INFO(("using 'realtime pit' synchronization method"));
+ }
+
+ register_timer(this, nullTimer, (Bit32u)NullTimerInterval, 1, 1, "Null Timer");
+
+ system_timer_id = bx_pc_system.register_timer(this, pc_system_timer_handler,virtual_next_event_time , 0, 1, "Virtual Timer");
+
+ //Real time variables:
+#if BX_HAVE_REALTIME_USEC
+ last_real_time=GET_VIRT_REALTIME64_USEC()+(Bit64u)TIME_HEADSTART*(Bit64u)USEC_PER_SECOND;
+#endif
+ total_real_usec=0;
+ last_realtime_delta=0;
+ //System time variables:
+ last_usec = 0
+;
+ usec_per_second = USEC_PER_SECOND;
+ stored_delta=0;
+ last_system_usec=0;
+ em_last_realtime=0;
+ //Virtual timer variables:
+ total_ticks=0;
+ last_realtime_ticks=0;
+ ticks_per_second = USEC_PER_SECOND;
+
+ init_done = 1;
+}
+
+void
+bx_virt_timer_c::timer_handler(void) {
+ if(!virtual_timers_realtime) {
+ Bit64u temp_final_time = bx_pc_system.time_usec();
+ temp_final_time-=current_virtual_time;
+ while(temp_final_time) {
+ if((temp_final_time)>(virtual_next_event_time)) {
+ temp_final_time-=virtual_next_event_time;
+ advance_virtual_time(virtual_next_event_time);
+ } else {
+ advance_virtual_time(temp_final_time);
+ temp_final_time-=temp_final_time;
+ }
+ }
+ bx_pc_system.activate_timer(system_timer_id,
+ (Bit32u)BX_MIN(0x7FFFFFFF,(virtual_next_event_time>2)?(virtual_next_event_time-2):1),
+ 0);
+ return;
+ }
+
+ Bit64u usec_delta = bx_pc_system.time_usec()-last_usec;
+
+ if (usec_delta) {
+#if BX_HAVE_REALTIME_USEC
+ Bit64u ticks_delta = 0;
+ Bit64u real_time_delta = GET_VIRT_REALTIME64_USEC() - last_real_time;
+ Bit64u real_time_total = real_time_delta + total_real_usec;
+ Bit64u system_time_delta = (Bit64u)usec_delta + (Bit64u)stored_delta;
+ if(real_time_delta) {
+ last_realtime_delta = real_time_delta;
+ last_realtime_ticks = total_ticks;
+ }
+ ticks_per_second = USEC_PER_SECOND;
+
+ //Start out with the number of ticks we would like
+ // to have to line up with real time.
+ ticks_delta = real_time_total - total_ticks;
+ if(real_time_total < total_ticks) {
+ //This slows us down if we're already ahead.
+ // probably only an issue on startup, but it solves some problems.
+ ticks_delta = 0;
+ }
+ if(ticks_delta + total_ticks - last_realtime_ticks > (F2I(MAX_MULT * I2F(last_realtime_delta)))) {
+ //This keeps us from going too fast in relation to real time.
+#if 0
+ ticks_delta = (F2I(MAX_MULT * I2F(last_realtime_delta))) + last_realtime_ticks - total_ticks;
+#endif
+ ticks_per_second = F2I(MAX_MULT * I2F(USEC_PER_SECOND));
+ }
+ if(ticks_delta > system_time_delta * USEC_PER_SECOND / MIN_USEC_PER_SECOND) {
+ //This keeps us from having too few instructions between ticks.
+ ticks_delta = system_time_delta * USEC_PER_SECOND / MIN_USEC_PER_SECOND;
+ }
+ if(ticks_delta > virtual_next_event_time) {
+ //This keeps us from missing ticks.
+ ticks_delta = virtual_next_event_time;
+ }
+
+ if(ticks_delta) {
+
+# if DEBUG_REALTIME_WITH_PRINTF
+ //Every second print some info.
+ if(((last_real_time + real_time_delta) / USEC_PER_SECOND) > (last_real_time / USEC_PER_SECOND)) {
+ Bit64u temp1, temp2, temp3, temp4;
+ temp1 = (Bit64u) total_real_usec;
+ temp2 = (total_real_usec);
+ temp3 = (Bit64u)total_ticks;
+ temp4 = (Bit64u)((total_real_usec) - total_ticks);
+ printf("useconds: %llu, ",temp1);
+ printf("expect ticks: %llu, ",temp2);
+ printf("ticks: %llu, ",temp3);
+ printf("diff: %llu\n",temp4);
+ }
+# endif
+
+ last_real_time += real_time_delta;
+ total_real_usec += real_time_delta;
+ last_system_usec += system_time_delta;
+ stored_delta = 0;
+ total_ticks += ticks_delta;
+ } else {
+ stored_delta = system_time_delta;
+ }
+
+
+ Bit64u a,b;
+ a=(usec_per_second);
+ if(real_time_delta) {
+ //FIXME
+ Bit64u em_realtime_delta = last_system_usec + stored_delta - em_last_realtime;
+ b=((Bit64u)USEC_PER_SECOND * em_realtime_delta / real_time_delta);
+ em_last_realtime = last_system_usec + stored_delta;
+ } else {
+ b=a;
+ }
+ usec_per_second = ALPHA_LOWER(a,b);
+#else
+ BX_ASSERT(0);
+#endif
+#if BX_HAVE_REALTIME_USEC
+ advance_virtual_time(ticks_delta);
+#endif
+ }
+
+ last_usec=last_usec + usec_delta;
+ bx_pc_system.deactivate_timer(system_timer_id);
+ BX_ASSERT(virtual_next_event_time);
+ bx_pc_system.activate_timer(system_timer_id,
+ (Bit32u)BX_MIN(0x7FFFFFFF,BX_MAX(1,TICKS_TO_USEC(virtual_next_event_time))),
+ 0);
+
+}
+
+void
+bx_virt_timer_c::pc_system_timer_handler(void* this_ptr) {
+ ((bx_virt_timer_c *)this_ptr)->timer_handler();
+}
+
diff --git a/tools/ioemu/iodev/virt_timer.h b/tools/ioemu/iodev/virt_timer.h
new file mode 100644
index 0000000000..a10b16cd0e
--- /dev/null
+++ b/tools/ioemu/iodev/virt_timer.h
@@ -0,0 +1,131 @@
+
+#ifndef _BX_VIRT_TIMER_H
+#define _BX_VIRT_TIMER_H
+
+
+#define BX_MAX_VIRTUAL_TIMERS (15+BX_SMP_PROCESSORS)
+#define BX_NULL_VIRTUAL_TIMER_HANDLE 10000
+
+#define BX_MAX_VIRTUAL_TIME (0x7fffffff)
+
+class bx_virt_timer_c : public logfunctions {
+ private:
+
+ struct {
+ bx_bool inUse; // Timer slot is in-use (currently registered).
+ Bit64u period; // Timer periodocity in virtual useconds.
+ Bit64u timeToFire; // Time to fire next (in virtual useconds).
+ bx_bool active; // 0=inactive, 1=active.
+ bx_bool continuous; // 0=one-shot timer, 1=continuous periodicity.
+ bx_timer_handler_t funct; // A callback function for when the
+ // timer fires.
+ // This function MUST return.
+ void *this_ptr; // The this-> pointer for C++ callbacks
+ // has to be stored as well.
+ char id[BxMaxTimerIDLen]; // String ID of timer.
+ } timer[BX_MAX_VIRTUAL_TIMERS];
+
+ unsigned numTimers; // Number of currently allocated timers.
+
+ //Variables for the timer subsystem:
+ Bit64u current_timers_time;
+ Bit64u timers_next_event_time;
+
+ Bit64u last_sequential_time;
+ bx_bool in_timer_handler;
+
+ //Variables for the time sync subsystem:
+ Bit64u virtual_next_event_time;
+ Bit64u current_virtual_time;
+
+ //Real time variables:
+ Bit64u last_real_time;
+ Bit64u total_real_usec;
+ Bit64u last_realtime_delta;
+ //System time variables:
+ Bit64u last_usec;
+ Bit64u usec_per_second;
+ Bit64u stored_delta;
+ Bit64u last_system_usec;
+ Bit64u em_last_realtime;
+ //Virtual timer variables:
+ Bit64u total_ticks;
+ Bit64u last_realtime_ticks;
+ Bit64u ticks_per_second;
+
+ bx_bool init_done;
+
+ int system_timer_id;
+
+ //Whether or not to use virtual timers.
+ bx_bool use_virtual_timers;
+ bx_bool virtual_timers_realtime;
+
+ // A special null timer is always inserted in the timer[0] slot. This
+ // make sure that at least one timer is always active, and that the
+ // duration is always less than a maximum 32-bit integer, so a 32-bit
+ // counter can be used for the current countdown.
+ static const Bit64u NullTimerInterval;
+ static void nullTimer(void* this_ptr);
+
+
+ //Step the given number of cycles, optionally calling any timer handlers.
+ void periodic(Bit64u time_passed);
+
+
+ //Called when next_event_time changes.
+ void next_event_time_update(void);
+
+ //Called to advance the virtual time.
+ // calls periodic as needed.
+ void advance_virtual_time(Bit64u time_passed);
+
+ public:
+
+
+ //Get the current virtual time.
+ // This may return the same value on subsequent calls.
+ Bit64u time_usec(void);
+
+ //Get the current virtual time.
+ // This will return a monotonically increasing value.
+ // MUST NOT be called from within a timer handler.
+ Bit64u time_usec_sequential(void);
+
+ //Register a timer handler to go off after a given interval.
+ //Register a timer handler to go off with a periodic interval.
+ int register_timer( void *this_ptr, bx_timer_handler_t handler,
+ Bit32u useconds,
+ bx_bool continuous, bx_bool active, const char *id);
+
+ //unregister a previously registered timer.
+ unsigned unregisterTimer(int timerID);
+
+ void start_timers(void);
+
+ //activate a deactivated but registered timer.
+ void activate_timer( unsigned timer_index, Bit32u useconds,
+ bx_bool continuous );
+
+ //deactivate (but don't unregister) a currently registered timer.
+ void deactivate_timer( unsigned timer_index );
+
+
+ //Timer handler passed to pc_system
+ static void pc_system_timer_handler(void* this_ptr);
+
+ //The real timer handler.
+ void timer_handler();
+
+ //Initialization
+ void init(void);
+ bx_virt_timer_c(void);
+ ~bx_virt_timer_c(void);
+
+};
+
+
+
+extern bx_virt_timer_c bx_virt_timer;
+
+#endif // _BX_VIRT_TIMER_H
diff --git a/tools/ioemu/memory/Makefile b/tools/ioemu/memory/Makefile
new file mode 100644
index 0000000000..eb9be7da4b
--- /dev/null
+++ b/tools/ioemu/memory/Makefile
@@ -0,0 +1,12 @@
+TOPDIR= ..
+CXXFLAGS=-I. -I../include -I..
+OBJS= memory.o misc_mem.o
+
+all: libmemory.a
+
+libmemory.a: $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+
+include $(TOPDIR)/mk/helix.mk
+
+install:: all
diff --git a/tools/ioemu/memory/memory.cc b/tools/ioemu/memory/memory.cc
new file mode 100644
index 0000000000..7ee55f360f
--- /dev/null
+++ b/tools/ioemu/memory/memory.cc
@@ -0,0 +1,450 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: memory.cc,v 1.27 2003/03/02 23:59:12 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+
+
+
+#include "bochs.h"
+#define LOG_THIS BX_MEM_THIS
+
+#if BX_PROVIDE_CPU_MEMORY
+
+ void BX_CPP_AttrRegparmN(3)
+BX_MEM_C::writePhysicalPage(BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data)
+{
+ Bit8u *data_ptr;
+ Bit32u a20addr;
+
+ // Note: accesses should always be contained within a single page now.
+
+#if BX_IODEBUG_SUPPORT
+ bx_iodebug_c::mem_write(cpu, addr, len, data);
+#endif
+ if (addr+len > this->dma_limit)
+ BX_PANIC(("address too large: %lx > %lx", addr+len, this->dma_limit));
+
+ a20addr = A20ADDR(addr);
+ BX_INSTR_PHY_WRITE(cpu->which_cpu(), a20addr, len);
+
+#if BX_DEBUGGER
+ // (mch) Check for physical write break points, TODO
+ // (bbd) Each breakpoint should have an associated CPU#, TODO
+ for (int i = 0; i < num_write_watchpoints; i++)
+ if (write_watchpoint[i] == a20addr) {
+ BX_CPU(0)->watchpoint = a20addr;
+ BX_CPU(0)->break_point = BREAK_POINT_WRITE;
+ break;
+ }
+#endif
+
+#if BX_SupportICache
+ if (a20addr < BX_MEM_THIS len)
+ cpu->iCache.decWriteStamp(cpu, a20addr);
+#endif
+
+ if ( a20addr <= BX_MEM_THIS len ) {
+ // all of data is within limits of physical memory
+ if ( (a20addr & 0xfff80000) != 0x00080000 ) {
+ if (len == 4) {
+ WriteHostDWordToLittleEndian(&vector[a20addr], *(Bit32u*)data);
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ return;
+ }
+ if (len == 2) {
+ WriteHostWordToLittleEndian(&vector[a20addr], *(Bit16u*)data);
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ return;
+ }
+ if (len == 1) {
+ * ((Bit8u *) (&vector[a20addr])) = * (Bit8u *) data;
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ return;
+ }
+ // len == other, just fall thru to special cases handling
+ }
+
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr = (Bit8u *) data;
+#else // BX_BIG_ENDIAN
+ data_ptr = (Bit8u *) data + (len - 1);
+#endif
+
+write_one:
+ if ( (a20addr & 0xfff80000) != 0x00080000 ) {
+ // addr *not* in range 00080000 .. 000FFFFF
+ vector[a20addr] = *data_ptr;
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+inc_one:
+ if (len == 1) return;
+ len--;
+ a20addr++;
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr++;
+#else // BX_BIG_ENDIAN
+ data_ptr--;
+#endif
+ goto write_one;
+ }
+
+ // addr in range 00080000 .. 000FFFFF
+
+ if (a20addr <= 0x0009ffff) {
+ // regular memory 80000 .. 9FFFF
+ vector[a20addr] = *data_ptr;
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ goto inc_one;
+ }
+ if (a20addr <= 0x000bffff) {
+ // VGA memory A0000 .. BFFFF
+ DEV_vga_mem_write(a20addr, *data_ptr);
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ BX_DBG_UCMEM_REPORT(a20addr, 1, BX_WRITE, *data_ptr); // obsolete
+ goto inc_one;
+ }
+ // adapter ROM C0000 .. DFFFF
+ // ROM BIOS memory E0000 .. FFFFF
+ // (ignore write)
+ //BX_INFO(("ROM lock %08x: len=%u",
+ // (unsigned) a20addr, (unsigned) len));
+#if BX_PCI_SUPPORT == 0
+#if BX_SHADOW_RAM
+ // Write it since its in shadow RAM
+ vector[a20addr] = *data_ptr;
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+#else
+ // ignore write to ROM
+#endif
+#else
+ // Write Based on 440fx Programming
+ if (bx_options.Oi440FXSupport->get () &&
+ ((a20addr >= 0xC0000) && (a20addr <= 0xFFFFF))) {
+ switch (DEV_pci_wr_memtype(a20addr & 0xFC000)) {
+ case 0x1: // Writes to ShadowRAM
+// BX_INFO(("Writing to ShadowRAM %08x, len %u ! ", (unsigned) a20addr, (unsigned) len));
+ shadow[a20addr - 0xc0000] = *data_ptr;
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ goto inc_one;
+
+ case 0x0: // Writes to ROM, Inhibit
+ BX_DEBUG(("Write to ROM ignored: address %08x, data %02x", (unsigned) a20addr, *data_ptr));
+ goto inc_one;
+ default:
+ BX_PANIC(("writePhysicalPage: default case"));
+ goto inc_one;
+ }
+ }
+#endif
+ goto inc_one;
+ }
+
+ else {
+ // some or all of data is outside limits of physical memory
+ unsigned i;
+
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr = (Bit8u *) data;
+#else // BX_BIG_ENDIAN
+ data_ptr = (Bit8u *) data + (len - 1);
+#endif
+
+
+#if BX_SUPPORT_VBE
+ // Check VBE LFB support
+
+ if ((a20addr >= VBE_DISPI_LFB_PHYSICAL_ADDRESS) &&
+ (a20addr < (VBE_DISPI_LFB_PHYSICAL_ADDRESS + VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)))
+ {
+ for (i = 0; i < len; i++) {
+
+ //if (a20addr < BX_MEM_THIS len) {
+ //vector[a20addr] = *data_ptr;
+ //BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ DEV_vga_mem_write(a20addr, *data_ptr);
+ // }
+
+ // otherwise ignore byte, since it overruns memory
+ addr++;
+ a20addr = (addr);
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr++;
+#else // BX_BIG_ENDIAN
+ data_ptr--;
+#endif
+ }
+ return;
+ }
+
+#endif
+
+
+#if BX_SUPPORT_APIC
+ bx_generic_apic_c *local_apic = &cpu->local_apic;
+ bx_generic_apic_c *ioapic = bx_devices.ioapic;
+ if (local_apic->is_selected (a20addr, len)) {
+ local_apic->write (a20addr, (Bit32u *)data, len);
+ return;
+ } else if (ioapic->is_selected (a20addr, len)) {
+ ioapic->write (a20addr, (Bit32u *)data, len);
+ return;
+ }
+ else
+#endif
+ for (i = 0; i < len; i++) {
+ if (a20addr < BX_MEM_THIS len) {
+ vector[a20addr] = *data_ptr;
+ BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ }
+ // otherwise ignore byte, since it overruns memory
+ addr++;
+ a20addr = (addr);
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr++;
+#else // BX_BIG_ENDIAN
+ data_ptr--;
+#endif
+ }
+ return;
+ }
+}
+
+
+ void BX_CPP_AttrRegparmN(3)
+BX_MEM_C::readPhysicalPage(BX_CPU_C *cpu, Bit32u addr, unsigned len, void *data)
+{
+ Bit8u *data_ptr;
+ Bit32u a20addr;
+
+#if BX_IODEBUG_SUPPORT
+ bx_iodebug_c::mem_read(cpu, addr, len, data);
+#endif
+
+ if (addr+len > this->dma_limit)
+ BX_PANIC(("address too large: %lx > %lx", addr+len, this->dma_limit));
+
+ a20addr = A20ADDR(addr);
+ BX_INSTR_PHY_READ(cpu->which_cpu(), a20addr, len);
+
+#if BX_DEBUGGER
+ // (mch) Check for physical read break points, TODO
+ // (bbd) Each breakpoint should have an associated CPU#, TODO
+ for (int i = 0; i < num_read_watchpoints; i++)
+ if (read_watchpoint[i] == a20addr) {
+ BX_CPU(0)->watchpoint = a20addr;
+ BX_CPU(0)->break_point = BREAK_POINT_READ;
+ break;
+ }
+#endif
+
+ if ( (a20addr + len) <= BX_MEM_THIS len ) {
+ // all of data is within limits of physical memory
+ if ( (a20addr & 0xfff80000) != 0x00080000 ) {
+ if (len == 4) {
+ ReadHostDWordFromLittleEndian(&vector[a20addr], * (Bit32u*) data);
+ return;
+ }
+ if (len == 2) {
+ ReadHostWordFromLittleEndian(&vector[a20addr], * (Bit16u*) data);
+ return;
+ }
+ if (len == 1) {
+ * (Bit8u *) data = * ((Bit8u *) (&vector[a20addr]));
+ return;
+ }
+ // len == 3 case can just fall thru to special cases handling
+ }
+
+
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr = (Bit8u *) data;
+#else // BX_BIG_ENDIAN
+ data_ptr = (Bit8u *) data + (len - 1);
+#endif
+
+
+
+read_one:
+ if ( (a20addr & 0xfff80000) != 0x00080000 ) {
+ // addr *not* in range 00080000 .. 000FFFFF
+ *data_ptr = vector[a20addr];
+inc_one:
+ if (len == 1) return;
+ len--;
+ a20addr++;
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr++;
+#else // BX_BIG_ENDIAN
+ data_ptr--;
+#endif
+ goto read_one;
+ }
+
+ // addr in range 00080000 .. 000FFFFF
+#if BX_PCI_SUPPORT == 0
+ if ((a20addr <= 0x0009ffff) || (a20addr >= 0x000c0000) ) {
+ // regular memory 80000 .. 9FFFF, C0000 .. F0000
+ *data_ptr = vector[a20addr];
+ goto inc_one;
+ }
+ // VGA memory A0000 .. BFFFF
+ *data_ptr = DEV_vga_mem_read(a20addr);
+ BX_DBG_UCMEM_REPORT(a20addr, 1, BX_READ, *data_ptr); // obsolete
+ goto inc_one;
+#else // #if BX_PCI_SUPPORT == 0
+ if (a20addr <= 0x0009ffff) {
+ *data_ptr = vector[a20addr];
+ goto inc_one;
+ }
+ if (a20addr <= 0x000BFFFF) {
+ // VGA memory A0000 .. BFFFF
+ *data_ptr = DEV_vga_mem_read(a20addr);
+ BX_DBG_UCMEM_REPORT(a20addr, 1, BX_READ, *data_ptr);
+ goto inc_one;
+ }
+
+ // a20addr in C0000 .. FFFFF
+ if (!bx_options.Oi440FXSupport->get ()) {
+ *data_ptr = vector[a20addr];
+ goto inc_one;
+ }
+ else {
+ switch (DEV_pci_rd_memtype(a20addr & 0xFC000)) {
+ case 0x1: // Read from ShadowRAM
+ *data_ptr = shadow[a20addr - 0xc0000];
+ BX_INFO(("Reading from ShadowRAM %08x, Data %02x ", (unsigned) a20addr, *data_ptr));
+ goto inc_one;
+
+ case 0x0: // Read from ROM
+ *data_ptr = vector[a20addr];
+ //BX_INFO(("Reading from ROM %08x, Data %02x ", (unsigned) a20addr, *data_ptr));
+ goto inc_one;
+ default:
+ BX_PANIC(("::readPhysicalPage: default case"));
+ }
+ }
+ goto inc_one;
+#endif // #if BX_PCI_SUPPORT == 0
+ }
+ else {
+ // some or all of data is outside limits of physical memory
+ unsigned i;
+
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr = (Bit8u *) data;
+#else // BX_BIG_ENDIAN
+ data_ptr = (Bit8u *) data + (len - 1);
+#endif
+
+#if BX_SUPPORT_VBE
+ // Check VBE LFB support
+
+ if ((a20addr >= VBE_DISPI_LFB_PHYSICAL_ADDRESS) &&
+ (a20addr < (VBE_DISPI_LFB_PHYSICAL_ADDRESS + VBE_DISPI_TOTAL_VIDEO_MEMORY_BYTES)))
+ {
+ for (i = 0; i < len; i++) {
+
+ //if (a20addr < BX_MEM_THIS len) {
+ //vector[a20addr] = *data_ptr;
+ //BX_DBG_DIRTY_PAGE(a20addr >> 12);
+ *data_ptr = DEV_vga_mem_read(a20addr);
+ // }
+
+ // otherwise ignore byte, since it overruns memory
+ addr++;
+ a20addr = (addr);
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr++;
+#else // BX_BIG_ENDIAN
+ data_ptr--;
+#endif
+ }
+ return;
+ }
+
+#endif
+
+
+#if BX_SUPPORT_APIC
+ bx_generic_apic_c *local_apic = &cpu->local_apic;
+ bx_generic_apic_c *ioapic = bx_devices.ioapic;
+ if (local_apic->is_selected (addr, len)) {
+ local_apic->read (addr, data, len);
+ return;
+ } else if (ioapic->is_selected (addr, len)) {
+ ioapic->read (addr, data, len);
+ return;
+ }
+#endif
+ for (i = 0; i < len; i++) {
+#if BX_PCI_SUPPORT == 0
+ if (a20addr < BX_MEM_THIS len)
+ *data_ptr = vector[a20addr];
+ else
+ *data_ptr = 0xff;
+#else // BX_PCI_SUPPORT == 0
+ if (a20addr < BX_MEM_THIS len) {
+ if ((a20addr >= 0x000C0000) && (a20addr <= 0x000FFFFF)) {
+ if (!bx_options.Oi440FXSupport->get ())
+ *data_ptr = vector[a20addr];
+ else {
+ switch (DEV_pci_rd_memtype(a20addr & 0xFC000)) {
+ case 0x0: // Read from ROM
+ *data_ptr = vector[a20addr];
+ //BX_INFO(("Reading from ROM %08x, Data %02x ", (unsigned) a20addr, *data_ptr));
+ break;
+
+ case 0x1: // Read from Shadow RAM
+ *data_ptr = shadow[a20addr - 0xc0000];
+ BX_INFO(("Reading from ShadowRAM %08x, Data %02x ", (unsigned) a20addr, *data_ptr));
+ break;
+ default:
+ BX_PANIC(("readPhysicalPage: default case"));
+ } // Switch
+ }
+ }
+ else {
+ *data_ptr = vector[a20addr];
+ BX_INFO(("Reading from Norm %08x, Data %02x ", (unsigned) a20addr, *data_ptr));
+ }
+ }
+ else
+ *data_ptr = 0xff;
+#endif // BX_PCI_SUPPORT == 0
+ addr++;
+ a20addr = (addr);
+#ifdef BX_LITTLE_ENDIAN
+ data_ptr++;
+#else // BX_BIG_ENDIAN
+ data_ptr--;
+#endif
+ }
+ return;
+ }
+}
+
+#endif // #if BX_PROVIDE_CPU_MEMORY
diff --git a/tools/ioemu/memory/memory.h b/tools/ioemu/memory/memory.h
new file mode 100644
index 0000000000..2af787b3c3
--- /dev/null
+++ b/tools/ioemu/memory/memory.h
@@ -0,0 +1,98 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: memory.h,v 1.16 2003/08/05 13:19:35 cbothamy Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2001 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+#define BX_USE_MEM_SMF 0
+
+#if BX_USE_MEM_SMF
+// if static member functions on, then there is only one memory
+# define BX_MEM_SMF static
+# define BX_MEM_THIS BX_MEM(0)->
+#else
+# define BX_MEM_SMF
+# define BX_MEM_THIS this->
+#endif
+
+// alignment of memory vector, must be a power of 2
+#define BX_MEM_VECTOR_ALIGN 4096
+
+class BOCHSAPI BX_MEM_C : public logfunctions {
+
+public:
+ Bit8u *actual_vector;
+ Bit8u *vector; // aligned correctly
+ size_t len;
+ size_t dma_limit;
+ size_t megabytes; // (len in Megabytes)
+#if BX_PCI_SUPPORT
+ Bit8u shadow[4*16*4096]; // 256k of memory
+#endif
+#if BX_DEBUGGER
+ unsigned char dbg_dirty_pages[(BX_MAX_DIRTY_PAGE_TABLE_MEGS * 1024 * 1024) / 4096];
+ Bit32u dbg_count_dirty_pages () {
+ return (BX_MAX_DIRTY_PAGE_TABLE_MEGS * 1024 * 1024) / 4096;
+ }
+#endif
+ unsigned long *page_array; /* for get_pfn_list() */
+
+ BX_MEM_C(void);
+ //BX_MEM_C(size_t memsize);
+ ~BX_MEM_C(void);
+ BX_MEM_SMF void alloc_vector_aligned (size_t bytes, size_t alignment) BX_CPP_AttrRegparmN(2);
+ BX_MEM_SMF void init_memory(int memsize);
+ BX_MEM_SMF void readPhysicalPage(BX_CPU_C *cpu, Bit32u addr,
+ unsigned len, void *data) BX_CPP_AttrRegparmN(3);
+ BX_MEM_SMF void writePhysicalPage(BX_CPU_C *cpu, Bit32u addr,
+ unsigned len, void *data) BX_CPP_AttrRegparmN(3);
+ BX_MEM_SMF void load_ROM(const char *path, Bit32u romaddress, Bit8u type);
+ BX_MEM_SMF Bit32u get_memory_in_k(void);
+#if BX_PCI_SUPPORT
+ BX_MEM_SMF Bit8u* pci_fetch_ptr(Bit32u addr) BX_CPP_AttrRegparmN(1);
+#endif
+ BX_MEM_SMF bx_bool dbg_fetch_mem(Bit32u addr, unsigned len, Bit8u *buf);
+ BX_MEM_SMF bx_bool dbg_set_mem(Bit32u addr, unsigned len, Bit8u *buf);
+ BX_MEM_SMF bx_bool dbg_crc32(
+ unsigned long (*f)(unsigned char *buf, int len),
+ Bit32u addr1, Bit32u addr2, Bit32u *crc);
+ BX_MEM_SMF Bit8u * getHostMemAddr(BX_CPU_C *cpu, Bit32u a20Addr, unsigned op) BX_CPP_AttrRegparmN(3);
+ };
+
+#if BX_PROVIDE_CPU_MEMORY==1
+
+#if BX_SMP_PROCESSORS==1
+BOCHSAPI extern BX_MEM_C bx_mem;
+#else
+BOCHSAPI extern BX_MEM_C *bx_mem_array[BX_ADDRESS_SPACES];
+#endif /* BX_SMP_PROCESSORS */
+
+#endif /* BX_PROVIDE_CPU_MEMORY==1 */
+
+#if BX_DEBUGGER
+# define BX_DBG_DIRTY_PAGE(page) BX_MEM(0)->dbg_dirty_pages[page] = 1;
+#else
+# define BX_DBG_DIRTY_PAGE(page)
+#endif
diff --git a/tools/ioemu/memory/misc_mem.cc b/tools/ioemu/memory/misc_mem.cc
new file mode 100644
index 0000000000..5d2b678531
--- /dev/null
+++ b/tools/ioemu/memory/misc_mem.cc
@@ -0,0 +1,440 @@
+/////////////////////////////////////////////////////////////////////////
+// $Id: misc_mem.cc,v 1.41 2003/09/10 16:34:56 vruppert Exp $
+/////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2002 MandrakeSoft S.A.
+//
+// MandrakeSoft S.A.
+// 43, rue d'Aboukir
+// 75002 Paris - France
+// http://www.linux-mandrake.com/
+// http://www.mandrakesoft.com/
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+
+
+
+
+#include "bochs.h"
+#ifdef BX_USE_VMX
+extern "C" {
+#include <sys/mman.h>
+}
+#endif
+
+#define LOG_THIS BX_MEM(0)->
+
+#if BX_PROVIDE_CPU_MEMORY
+ Bit32u
+BX_MEM_C::get_memory_in_k(void)
+{
+ return(BX_MEM_THIS megabytes * 1024);
+}
+#endif // #if BX_PROVIDE_CPU_MEMORY
+
+
+#if BX_PROVIDE_CPU_MEMORY
+ // BX_MEM_C constructor
+BX_MEM_C::BX_MEM_C(void)
+{
+ char mem[6];
+ snprintf(mem, 6, "MEM%d", BX_SIM_ID);
+ put(mem);
+ settype(MEMLOG);
+
+ vector = NULL;
+ actual_vector = NULL;
+ len = 0;
+ megabytes = 0;
+}
+#endif // #if BX_PROVIDE_CPU_MEMORY
+
+
+
+#if BX_PROVIDE_CPU_MEMORY
+void BX_CPP_AttrRegparmN(2)
+BX_MEM_C::alloc_vector_aligned (size_t bytes, size_t alignment)
+{
+ if (actual_vector != NULL) {
+ BX_INFO (("freeing existing memory vector"));
+ delete [] actual_vector;
+ actual_vector = NULL;
+ vector = NULL;
+ }
+ Bit64u test_mask = alignment - 1;
+ actual_vector = new Bit8u [bytes+test_mask];
+ // round address forward to nearest multiple of alignment. Alignment
+ // MUST BE a power of two for this to work.
+ unsigned long masked = ((unsigned long)(actual_vector + test_mask)) & ~test_mask;
+ vector = (Bit8u *)masked;
+ // sanity check: no lost bits during pointer conversion
+ BX_ASSERT (sizeof(masked) >= sizeof(vector));
+ // sanity check: after realignment, everything fits in allocated space
+ BX_ASSERT (vector+bytes <= actual_vector+bytes+test_mask);
+ BX_INFO (("allocated memory at %p. after alignment, vector=%p",
+ actual_vector, vector));
+}
+#endif
+
+// We can't use this because alloc_vector_aligned uses BX_INFO, but the object does not yet exists
+/*
+#if BX_PROVIDE_CPU_MEMORY
+ // BX_MEM_C constructor
+
+BX_MEM_C::BX_MEM_C(size_t memsize)
+{
+ char mem[6];
+ snprintf(mem, 6, "MEM%d", BX_SIM_ID);
+ put(mem);
+ settype(MEMLOG);
+
+ vector = NULL;
+ actual_vector = NULL;
+ alloc_vector_aligned (memsize, BX_MEM_VECTOR_ALIGN);
+ len = memsize;
+ megabytes = len / (1024*1024);
+}
+#endif // #if BX_PROVIDE_CPU_MEMORY
+*/
+
+
+#if BX_PROVIDE_CPU_MEMORY
+// BX_MEM_C destructor
+BX_MEM_C::~BX_MEM_C(void)
+{
+ if (this-> vector != NULL) {
+ delete [] actual_vector;
+ actual_vector = NULL;
+ vector = NULL;
+ }
+ else {
+ BX_DEBUG(("(%u) memory not freed as it wasn't allocated!", BX_SIM_ID));
+ }
+}
+#endif // #if BX_PROVIDE_CPU_MEMORY
+
+
+#if BX_PROVIDE_CPU_MEMORY
+ void
+BX_MEM_C::init_memory(int memsize)
+{
+ BX_DEBUG(("Init $Id: misc_mem.cc,v 1.41 2003/09/10 16:34:56 vruppert Exp $"));
+ // you can pass 0 if memory has been allocated already through
+ // the constructor, or the desired size of memory if it hasn't
+ // BX_INFO(("%.2fMB", (float)(BX_MEM_THIS megabytes) ));
+
+#ifndef BX_USE_VMX
+ if (BX_MEM_THIS vector == NULL) {
+ // memory not already allocated, do now...
+ alloc_vector_aligned (memsize, BX_MEM_VECTOR_ALIGN);
+#endif
+ BX_MEM_THIS len = memsize;
+ BX_MEM_THIS megabytes = memsize / (1024*1024);
+ BX_INFO(("%.2fMB", (float)(BX_MEM_THIS megabytes) ));
+#ifndef BX_USE_VMX
+ }
+#endif
+
+#if BX_DEBUGGER
+ if (megabytes > BX_MAX_DIRTY_PAGE_TABLE_MEGS) {
+ BX_INFO(("Error: memory larger than dirty page table can handle"));
+ BX_PANIC(("Error: increase BX_MAX_DIRTY_PAGE_TABLE_MEGS"));
+ }
+#endif
+
+ unsigned long nr_pages = megabytes * (1024 * 1024/getpagesize());
+
+ if ( (page_array = (unsigned long *)
+ malloc(nr_pages * sizeof(unsigned long))) == NULL)
+ {
+ BX_ERROR(("Could not allocate memory"));
+ return;
+ }
+
+ if ( xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages )
+ {
+ BX_ERROR(("Could not get the page frame list"));
+ return;
+ }
+
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1 << PAGE_SHIFT)
+
+ if ((vector = (Bit8u *) xc_map_foreign_batch(xc_handle, domid,
+ PROT_READ|PROT_WRITE,
+ page_array,
+ nr_pages - 1)) == 0) {
+ BX_ERROR(("Could not map guest physical"));
+ return;
+ }
+
+ BX_MEM_THIS dma_limit = (nr_pages - 1) << PAGE_SHIFT;
+ BX_INFO(("DMA limit: %lx", BX_MEM_THIS dma_limit));
+
+ shared_page = xc_map_foreign_range(xc_handle, domid, PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ page_array[nr_pages - 1]);
+}
+#endif // #if BX_PROVIDE_CPU_MEMORY
+
+
+#if BX_PROVIDE_CPU_MEMORY
+ void
+ // Values for type :
+ // 0 : System Bios
+ // 1 : VGA Bios
+ // 2 : Optional ROM Bios
+BX_MEM_C::load_ROM(const char *path, Bit32u romaddress, Bit8u type)
+{
+ struct stat stat_buf;
+ int fd, ret;
+ unsigned long size, offset;
+
+ if (*path == '\0') {
+ if (type == 2) {
+ BX_PANIC(( "ROM: Optional BIOS image undefined."));
+ }
+ else if (type == 1) {
+ BX_PANIC(( "ROM: VGA BIOS image undefined."));
+ }
+ else {
+ BX_PANIC(( "ROM: System BIOS image undefined."));
+ }
+ return;
+ }
+ // read in ROM BIOS image file
+ fd = open(path, O_RDONLY
+#ifdef O_BINARY
+ | O_BINARY
+#endif
+ );
+ if (fd < 0) {
+ if (type < 2) {
+ BX_PANIC(( "ROM: couldn't open ROM image file '%s'.", path));
+ }
+ else {
+ BX_ERROR(( "ROM: couldn't open ROM image file '%s'.", path));
+ }
+ return;
+ }
+ ret = fstat(fd, &stat_buf);
+ if (ret) {
+ if (type < 2) {
+ BX_PANIC(( "ROM: couldn't stat ROM image file '%s'.", path));
+ }
+ else {
+ BX_ERROR(( "ROM: couldn't stat ROM image file '%s'.", path));
+ }
+ return;
+ }
+
+ size = stat_buf.st_size;
+
+ if ( (romaddress + size) > BX_MEM_THIS len ) {
+ BX_PANIC(( "ROM: ROM address range > physical memsize!"));
+ return;
+ }
+
+ offset = 0;
+ while (size > 0) {
+ ret = read(fd, (bx_ptr_t) &BX_MEM_THIS vector[romaddress + offset], size);
+ if (ret <= 0) {
+ BX_PANIC(( "ROM: read failed on BIOS image: '%s'",path));
+ }
+ size -= ret;
+ offset += ret;
+ }
+ close(fd);
+ BX_INFO(("rom at 0x%05x/%u ('%s')",
+ (unsigned) romaddress,
+ (unsigned) stat_buf.st_size,
+ path
+ ));
+}
+#endif // #if BX_PROVIDE_CPU_MEMORY
+
+#if BX_PCI_SUPPORT
+ Bit8u* BX_CPP_AttrRegparmN(1)
+BX_MEM_C::pci_fetch_ptr(Bit32u addr)
+{
+ if (bx_options.Oi440FXSupport->get ()) {
+ switch (DEV_pci_rd_memtype (addr)) {
+ case 0x1: // Read from ShadowRAM
+ return (&BX_MEM_THIS shadow[addr - 0xc0000]);
+
+ case 0x0: // Read from ROM
+ return (&BX_MEM_THIS vector[addr]);
+ default:
+ BX_PANIC(("pci_fetch_ptr(): default case"));
+ return(0);
+ }
+ }
+ else
+ return (&BX_MEM_THIS vector[addr]);
+}
+#endif
+
+
+#if ( BX_DEBUGGER || BX_DISASM || BX_GDBSTUB)
+ bx_bool
+BX_MEM_C::dbg_fetch_mem(Bit32u addr, unsigned len, Bit8u *buf)
+{
+ if ( (addr + len) > this->len ) {
+ BX_INFO(("dbg_fetch_mem out of range. 0x%x > 0x%x",
+ addr+len, this->len));
+ return(0); // error, beyond limits of memory
+ }
+ for (; len>0; len--) {
+ if ( (addr & 0xfffe0000) == 0x000a0000 ) {
+ *buf = DEV_vga_mem_read(addr);
+ }
+ else {
+#if BX_PCI_SUPPORT == 0
+ *buf = vector[addr];
+#else
+ if ( bx_options.Oi440FXSupport->get () &&
+ ((addr >= 0x000C0000) && (addr <= 0x000FFFFF)) ) {
+ switch (DEV_pci_rd_memtype (addr)) {
+ case 0x1: // Fetch from ShadowRAM
+ *buf = shadow[addr - 0xc0000];
+// BX_INFO(("Fetching from ShadowRAM %06x, len %u !", (unsigned)addr, (unsigned)len));
+ break;
+
+ case 0x0: // Fetch from ROM
+ *buf = vector[addr];
+// BX_INFO(("Fetching from ROM %06x, Data %02x ", (unsigned)addr, *buf));
+ break;
+ default:
+ BX_PANIC(("dbg_fetch_mem: default case"));
+ }
+ }
+ else
+ *buf = vector[addr];
+#endif // #if BX_PCI_SUPPORT == 0
+ }
+ buf++;
+ addr++;
+ }
+ return(1);
+}
+#endif
+
+#if BX_DEBUGGER || BX_GDBSTUB
+ bx_bool
+BX_MEM_C::dbg_set_mem(Bit32u addr, unsigned len, Bit8u *buf)
+{
+ if ( (addr + len) > this->len ) {
+ return(0); // error, beyond limits of memory
+ }
+ for (; len>0; len--) {
+ if ( (addr & 0xfffe0000) == 0x000a0000 ) {
+ DEV_vga_mem_write(addr, *buf);
+ }
+ else
+ vector[addr] = *buf;
+ buf++;
+ addr++;
+ }
+ return(1);
+}
+#endif
+
+ bx_bool
+BX_MEM_C::dbg_crc32(unsigned long (*f)(unsigned char *buf, int len),
+ Bit32u addr1, Bit32u addr2, Bit32u *crc)
+{
+ unsigned len;
+
+ *crc = 0;
+ if (addr1 > addr2)
+ return(0);
+
+ if (addr2 >= this->len) {
+ return(0); // error, specified address past last phy mem addr
+ }
+
+ len = 1 + addr2 - addr1;
+ *crc = f(vector + addr1, len);
+
+ return(1);
+}
+
+
+ Bit8u * BX_CPP_AttrRegparmN(3)
+BX_MEM_C::getHostMemAddr(BX_CPU_C *cpu, Bit32u a20Addr, unsigned op)
+ // Return a host address corresponding to the guest physical memory
+ // address (with A20 already applied), given that the calling
+ // code will perform an 'op' operation. This address will be
+ // used for direct access to guest memory as an acceleration by
+ // a few instructions, like REP {MOV, INS, OUTS, etc}.
+ // Values of 'op' are { BX_READ, BX_WRITE, BX_RW }.
+
+ // The other assumption is that the calling code _only_ accesses memory
+ // directly within the page that encompasses the address requested.
+{
+ if ( a20Addr >= BX_MEM_THIS len )
+ return(NULL); // Error, requested addr is out of bounds.
+ if (op == BX_READ) {
+ if ( (a20Addr > 0x9ffff) && (a20Addr < 0xc0000) )
+ return(NULL); // Vetoed! Mem mapped IO (VGA)
+#if !BX_PCI_SUPPORT
+ return( (Bit8u *) & vector[a20Addr] );
+#else
+ else if ( (a20Addr < 0xa0000) || (a20Addr > 0xfffff)
+ || (!bx_options.Oi440FXSupport->get ()) )
+ return( (Bit8u *) & vector[a20Addr] );
+ else {
+ switch (DEV_pci_rd_memtype (a20Addr)) {
+ case 0x0: // Read from ROM
+ return ( (Bit8u *) & vector[a20Addr]);
+ case 0x1: // Read from ShadowRAM
+ return( (Bit8u *) & shadow[a20Addr - 0xc0000]);
+ default:
+ BX_PANIC(("getHostMemAddr(): default case"));
+ return(0);
+ }
+ }
+#endif
+ }
+ else { // op == {BX_WRITE, BX_RW}
+ Bit8u *retAddr;
+
+ if ( (a20Addr < 0xa0000) || (a20Addr > 0xfffff) ) {
+ retAddr = (Bit8u *) & vector[a20Addr];
+ }
+#if !BX_PCI_SUPPORT
+ else
+ return(NULL); // Vetoed! Mem mapped IO (VGA) and ROMs
+#else
+ else if ( (a20Addr < 0xc0000) || (!bx_options.Oi440FXSupport->get ()) )
+ return(NULL); // Vetoed! Mem mapped IO (VGA) and ROMs
+ else if (DEV_pci_wr_memtype (a20Addr) == 1) {
+ // Write to ShadowRAM
+ retAddr = (Bit8u *) & shadow[a20Addr - 0xc0000];
+ }
+ else
+ return(NULL); // Vetoed! ROMs
+#endif
+
+#if BX_SupportICache
+ cpu->iCache.decWriteStamp(cpu, a20Addr);
+#endif
+
+ return(retAddr);
+ }
+}
diff --git a/tools/ioemu/mk/helix.mk b/tools/ioemu/mk/helix.mk
new file mode 100644
index 0000000000..e18eb340d2
--- /dev/null
+++ b/tools/ioemu/mk/helix.mk
@@ -0,0 +1,6 @@
+CXXFLAGS += -O2 -I../../../tools/libxc -I../../../xen/include/public
+clean:
+ $(RM) -f *.o *~ lib*.a device-model
+
+install::
+
diff --git a/tools/libxc/Makefile b/tools/libxc/Makefile
index 770ecb4225..7ef744aa95 100644
--- a/tools/libxc/Makefile
+++ b/tools/libxc/Makefile
@@ -1,18 +1,24 @@
-MAJOR = 1.3
+ifndef BUILD_PIC_LIBS
+ifeq ($(wildcard /etc/debian_version),)
+BUILD_PIC_LIBS=n
+else
+BUILD_PIC_LIBS=y
+endif
+endif
+
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DATA = $(INSTALL) -m0644
+INSTALL_DIR = $(INSTALL) -d -m0755
+
+MAJOR = 3.0
MINOR = 0
-SONAME = libxc.so.$(MAJOR)
CC = gcc
XEN_ROOT = ../..
-include $(XEN_ROOT)/tools/Make.defs
-
-vpath %.h $(XEN_HYPERVISOR_IFS)
-INCLUDES += -I $(XEN_HYPERVISOR_IFS)
-
-vpath %h $(XEN_LINUX_INCLUDE)
-INCLUDES += -I $(XEN_LINUX_INCLUDE)
+include $(XEN_ROOT)/tools/Rules.mk
vpath %c $(XEN_LIBXUTIL)
INCLUDES += -I $(XEN_LIBXUTIL)
@@ -20,33 +26,35 @@ INCLUDES += -I $(XEN_LIBXUTIL)
SRCS :=
SRCS += xc_atropos.c
SRCS += xc_bvtsched.c
-SRCS += xc_fbvtsched.c
SRCS += xc_domain.c
SRCS += xc_evtchn.c
SRCS += xc_io.c
SRCS += xc_linux_build.c
+SRCS += xc_plan9_build.c
SRCS += xc_linux_restore.c
SRCS += xc_linux_save.c
SRCS += xc_misc.c
-SRCS += xc_netbsd_build.c
SRCS += xc_physdev.c
SRCS += xc_private.c
SRCS += xc_rrobin.c
+SRCS += xc_vmx_build.c
CFLAGS += -Wall
CFLAGS += -Werror
CFLAGS += -O3
CFLAGS += -fno-strict-aliasing
-CFLAGS += $(INCLUDES)
+CFLAGS += $(INCLUDES) -I.
# Get gcc to generate the dependencies for us.
CFLAGS += -Wp,-MD,.$(@F).d
DEPS = .*.d
-OBJS = $(patsubst %.c,%.o,$(SRCS))
+LIB_OBJS := $(patsubst %.c,%.o,$(SRCS))
+PIC_OBJS := $(patsubst %.c,%.opic,$(SRCS))
-LIB = libxc.so libxc.so.$(MAJOR) libxc.so.$(MAJOR).$(MINOR)
+LIB := libxc.a libxc.so libxc.so.$(MAJOR) libxc.so.$(MAJOR).$(MINOR)
-all: check-for-zlib $(LIB)
+all: check-for-zlib mk-symlinks
+ $(MAKE) $(LIB)
check-for-zlib:
@if [ ! -e /usr/include/zlib.h ]; then \
@@ -56,16 +64,28 @@ check-for-zlib:
false; \
fi
+LINUX_ROOT := $(wildcard $(XEN_ROOT)/linux-2.6.*-xen-sparse)
+mk-symlinks:
+ [ -e xen/linux ] || mkdir -p xen/linux
+ [ -e xen/io ] || mkdir -p xen/io
+ ( cd xen >/dev/null ; \
+ ln -sf ../$(XEN_ROOT)/xen/include/public/*.h . )
+ ( cd xen/io >/dev/null ; \
+ ln -sf ../../$(XEN_ROOT)/xen/include/public/io/*.h . )
+ ( cd xen/linux >/dev/null ; \
+ ln -sf ../../$(LINUX_ROOT)/include/asm-xen/linux-public/*.h . )
+
install: all
- mkdir -p $(prefix)/usr/lib
- mkdir -p $(prefix)/usr/include
- install -m0755 $(LIB) $(prefix)/usr/lib
- install -m0644 xc.h $(prefix)/usr/include
+ [ -d $(DESTDIR)/usr/lib ] || $(INSTALL_DIR) $(DESTDIR)/usr/lib
+ [ -d $(DESTDIR)/usr/include ] || $(INSTALL_DIR) $(DESTDIR)/usr/include
+ $(INSTALL_PROG) libxc.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/lib
+ $(INSTALL_DATA) libxc.a $(DESTDIR)/usr/lib
+ ln -sf libxc.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/lib/libxc.so.$(MAJOR)
+ ln -sf libxc.so.$(MAJOR) $(DESTDIR)/usr/lib/libxc.so
+ $(INSTALL_DATA) xc.h $(DESTDIR)/usr/include
clean:
- $(RM) *.a *.so *.o *.rpm $(LIB)
- $(RM) *~
- $(RM) $(DEPS)
+ rm -rf *.a *.so* *.o *.opic *.rpm $(LIB) *~ $(DEPS) xen
rpm: all
rm -rf staging
@@ -76,11 +96,23 @@ rpm: all
mv staging/i386/*.rpm .
rm -rf staging
-libxc.so:
- ln -sf libxc.so.$(MAJOR) $@
-libxc.so.$(MAJOR):
- ln -sf libxc.so.$(MAJOR).$(MINOR) $@
-libxc.so.$(MAJOR).$(MINOR): $(OBJS)
- $(CC) -Wl,-soname -Wl,$(SONAME) -shared -o $@ $^ -L../libxutil -lxutil -lz
+$(PIC_OBJS): %.opic: %.c
+ $(CC) $(CPPFLAGS) -DPIC $(CFLAGS) -fPIC -c -o $@ $<
+
+libxc.a: $(OBJS)
+ $(AR) rc $@ $^
+
+libxc.so: libxc.so.$(MAJOR)
+ ln -sf $< $@
+libxc.so.$(MAJOR): libxc.so.$(MAJOR).$(MINOR)
+ ln -sf $< $@
+
+ifeq ($(BUILD_PIC_LIBS),y)
+libxc.so.$(MAJOR).$(MINOR): $(PIC_OBJS)
+ $(CC) -Wl,-soname -Wl,libxc.so.$(MAJOR) -shared -o $@ $^ -L../libxutil -lxutil -lz
+else
+libxc.so.$(MAJOR).$(MINOR): $(LIB_OBJS)
+ $(CC) -Wl,-soname -Wl,libxc.so.$(MAJOR) -shared -o $@ $^ -L../libxutil -lxutil -lz
+endif
-include $(DEPS)
diff --git a/tools/libxc/linux_boot_params.h b/tools/libxc/linux_boot_params.h
new file mode 100644
index 0000000000..9b0b25cef9
--- /dev/null
+++ b/tools/libxc/linux_boot_params.h
@@ -0,0 +1,165 @@
+#ifndef __LINUX_BOOT_PARAMS_H__
+#define __LINUX_BOOT_PARAMS_H__
+
+#include <asm/types.h>
+
+#define E820MAX 32
+
+struct mem_map {
+ int nr_map;
+ struct entry {
+ unsigned long long addr; /* start of memory segment */
+ unsigned long long size; /* size of memory segment */
+ unsigned long type; /* type of memory segment */
+#define E820_RAM 1
+#define E820_RESERVED 2
+#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */
+#define E820_NVS 4
+#define E820_IO 16
+#define E820_SHARED 17
+
+ unsigned long caching_attr; /* used by hypervisor */
+#define MEMMAP_UC 0
+#define MEMMAP_WC 1
+#define MEMMAP_WT 4
+#define MEMMAP_WP 5
+#define MEMMAP_WB 6
+
+ }map[E820MAX];
+};
+
+struct e820entry {
+ unsigned long long addr; /* start of memory segment */
+ unsigned long long size; /* size of memory segment */
+ unsigned long type; /* type of memory segment */
+};
+
+struct e820map {
+ int nr_map;
+ struct e820entry map[E820MAX];
+};
+
+struct drive_info_struct { __u8 dummy[32]; };
+
+struct sys_desc_table {
+ __u16 length;
+ __u8 table[318];
+};
+
+struct screen_info {
+ unsigned char orig_x; /* 0x00 */
+ unsigned char orig_y; /* 0x01 */
+ unsigned short dontuse1; /* 0x02 -- EXT_MEM_K sits here */
+ unsigned short orig_video_page; /* 0x04 */
+ unsigned char orig_video_mode; /* 0x06 */
+ unsigned char orig_video_cols; /* 0x07 */
+ unsigned short unused2; /* 0x08 */
+ unsigned short orig_video_ega_bx; /* 0x0a */
+ unsigned short unused3; /* 0x0c */
+ unsigned char orig_video_lines; /* 0x0e */
+ unsigned char orig_video_isVGA; /* 0x0f */
+ unsigned short orig_video_points; /* 0x10 */
+
+ /* VESA graphic mode -- linear frame buffer */
+ unsigned short lfb_width; /* 0x12 */
+ unsigned short lfb_height; /* 0x14 */
+ unsigned short lfb_depth; /* 0x16 */
+ unsigned long lfb_base; /* 0x18 */
+ unsigned long lfb_size; /* 0x1c */
+ unsigned short dontuse2, dontuse3; /* 0x20 -- CL_MAGIC and CL_OFFSET here */
+ unsigned short lfb_linelength; /* 0x24 */
+ unsigned char red_size; /* 0x26 */
+ unsigned char red_pos; /* 0x27 */
+ unsigned char green_size; /* 0x28 */
+ unsigned char green_pos; /* 0x29 */
+ unsigned char blue_size; /* 0x2a */
+ unsigned char blue_pos; /* 0x2b */
+ unsigned char rsvd_size; /* 0x2c */
+ unsigned char rsvd_pos; /* 0x2d */
+ unsigned short vesapm_seg; /* 0x2e */
+ unsigned short vesapm_off; /* 0x30 */
+ unsigned short pages; /* 0x32 */
+ /* 0x34 -- 0x3f reserved for future expansion */
+};
+
+struct screen_info_overlap {
+ __u8 reserved1[2]; /* 0x00 */
+ __u16 ext_mem_k; /* 0x02 */
+ __u8 reserved2[0x20 - 0x04]; /* 0x04 */
+ __u16 cl_magic; /* 0x20 */
+#define CL_MAGIC_VALUE 0xA33F
+ __u16 cl_offset; /* 0x22 */
+ __u8 reserved3[0x40 - 0x24]; /* 0x24 */
+};
+
+
+struct apm_bios_info {
+ __u16 version;
+ __u16 cseg;
+ __u32 offset;
+ __u16 cseg_16;
+ __u16 dseg;
+ __u16 flags;
+ __u16 cseg_len;
+ __u16 cseg_16_len;
+ __u16 dseg_len;
+};
+
+struct linux_boot_params {
+ union { /* 0x00 */
+ struct screen_info info;
+ struct screen_info_overlap overlap;
+ } screen;
+
+ struct apm_bios_info apm_bios_info; /* 0x40 */
+ __u8 reserved4[0x80 - 0x54]; /* 0x54 */
+ struct drive_info_struct drive_info; /* 0x80 */
+ struct sys_desc_table sys_desc_table; /* 0xa0 */
+ __u32 alt_mem_k; /* 0x1e0 */
+ __u8 reserved5[4]; /* 0x1e4 */
+ __u8 e820_map_nr; /* 0x1e8 */
+ __u8 reserved6[8]; /* 0x1e9 */
+ __u8 setup_sects; /* 0x1f1 */
+ __u16 mount_root_rdonly; /* 0x1f2 */
+ __u16 syssize; /* 0x1f4 */
+ __u16 swapdev; /* 0x1f6 */
+ __u16 ramdisk_flags; /* 0x1f8 */
+#define RAMDISK_IMAGE_START_MASK 0x07FF
+#define RAMDISK_PROMPT_FLAG 0x8000
+#define RAMDISK_LOAD_FLAG 0x4000
+ __u16 vid_mode; /* 0x1fa */
+ __u16 root_dev; /* 0x1fc */
+ __u8 reserved9[1]; /* 0x1fe */
+ __u8 aux_device_info; /* 0x1ff */
+ /* 2.00+ */
+ __u8 reserved10[2]; /* 0x200 */
+ __u8 header_magic[4]; /* 0x202 */
+ __u16 protocol_version; /* 0x206 */
+ __u8 reserved11[8]; /* 0x208 */
+ __u8 loader_type; /* 0x210 */
+#define LOADER_TYPE_LOADLIN 1
+#define LOADER_TYPE_BOOTSECT_LOADER 2
+#define LOADER_TYPE_SYSLINUX 3
+#define LOADER_TYPE_ETHERBOOT 4
+#define LOADER_TYPE_UNKNOWN 0xFF
+ __u8 loader_flags; /* 0x211 */
+ __u8 reserved12[2]; /* 0x212 */
+ __u32 code32_start; /* 0x214 */
+ __u32 initrd_start; /* 0x218 */
+ __u32 initrd_size; /* 0x21c */
+ __u8 reserved13[4]; /* 0x220 */
+ /* 2.01+ */
+ __u16 heap_end_ptr; /* 0x224 */
+ __u8 reserved14[2]; /* 0x226 */
+ /* 2.02+ */
+ __u32 cmd_line_ptr; /* 0x228 */
+ /* 2.03+ */
+ __u32 ramdisk_max; /* 0x22c */
+ __u8 reserved15[0x2d0 - 0x230]; /* 0x230 */
+ struct e820entry e820_map[E820MAX]; /* 0x2d0 */
+ __u64 shared_info; /* 0x550 */
+ __u8 padding[0x800 - 0x558]; /* 0x558 */
+ __u8 cmd_line[0x800]; /* 0x800 */
+} __attribute__((packed));
+
+#endif /* __LINUX_BOOT_PARAMS_H__ */
diff --git a/tools/libxc/plan9a.out.h b/tools/libxc/plan9a.out.h
new file mode 100755
index 0000000000..d53f636517
--- /dev/null
+++ b/tools/libxc/plan9a.out.h
@@ -0,0 +1,28 @@
+
+typedef struct Exec
+{
+ long magic; /* magic number */
+ long text; /* size of text segment */
+ long data; /* size of initialized data */
+ long bss; /* size of uninitialized data */
+ long syms; /* size of symbol table */
+ long entry; /* entry point */
+ long spsz; /* size of pc/sp offset table */
+ long pcsz; /* size of pc/line number table */
+} Exec;
+
+#define _MAGIC(b) ((((4*b)+0)*b)+7)
+#define A_MAGIC _MAGIC(8) /* 68020 */
+#define I_MAGIC _MAGIC(11) /* intel 386 */
+#define J_MAGIC _MAGIC(12) /* intel 960 */
+#define K_MAGIC _MAGIC(13) /* sparc */
+#define V_MAGIC _MAGIC(16) /* mips 3000 */
+#define X_MAGIC _MAGIC(17) /* att dsp 3210 */
+#define M_MAGIC _MAGIC(18) /* mips 4000 */
+#define D_MAGIC _MAGIC(19) /* amd 29000 */
+#define E_MAGIC _MAGIC(20) /* arm 7-something */
+#define Q_MAGIC _MAGIC(21) /* powerpc */
+#define N_MAGIC _MAGIC(22) /* mips 4000 LE */
+#define L_MAGIC _MAGIC(23) /* dec alpha */
+#define P_MAGIC _MAGIC(24) /* mips 3000 LE */
+
diff --git a/tools/libxc/xc.h b/tools/libxc/xc.h
index 8b7ee33273..81d7b2ed3f 100644
--- a/tools/libxc/xc.h
+++ b/tools/libxc/xc.h
@@ -3,25 +3,62 @@
*
* A library for low-level access to the Xen control interfaces.
*
- * Copyright (c) 2003, K A Fraser.
+ * Copyright (c) 2003-2004, K A Fraser.
*/
#ifndef __XC_H__
#define __XC_H__
-typedef unsigned char u8;
-typedef unsigned short u16;
-typedef unsigned long u32;
-typedef unsigned long long u64;
-typedef signed char s8;
-typedef signed short s16;
-typedef signed long s32;
-typedef signed long long s64;
-
-/* Obtain or relinquish a handle on the 'xc' library. */
+#include <stdint.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+#include <xen/xen.h>
+#include <xen/dom0_ops.h>
+#include <xen/event_channel.h>
+#include <xen/sched_ctl.h>
+
+/*\
+ * INITIALIZATION FUNCTIONS
+\*/
+
+/**
+ * This function opens a handle to the hypervisor interface. This function can
+ * be called multiple times within a single process. Multiple processes can
+ * have an open hypervisor interface at the same time.
+ *
+ * Each call to this function should have a corresponding call to
+ * xc_interface_close().
+ *
+ * This function can fail if the caller does not have superuser permission or
+ * if a Xen-enabled kernel is not currently running.
+ *
+ * @return a handle to the hypervisor interface or -1 on failure
+ */
int xc_interface_open(void);
+
+/**
+ * This function closes an open hypervisor interface.
+ *
+ * This function can fail if the handle does not represent an open interface or
+ * if there were problems closing the interface.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @return 0 on success, -1 otherwise.
+ */
int xc_interface_close(int xc_handle);
+/*\
+ * DOMAIN MANAGEMENT FUNCTIONS
+\*/
+
typedef struct {
u32 domid;
unsigned int cpu;
@@ -31,38 +68,92 @@ typedef struct {
unsigned long nr_pages;
unsigned long shared_info_frame;
u64 cpu_time;
-#define XC_DOMINFO_MAXNAME 16
- char name[XC_DOMINFO_MAXNAME];
unsigned long max_memkb;
} xc_dominfo_t;
-typedef struct xc_shadow_control_stats_st
-{
- unsigned long fault_count;
- unsigned long dirty_count;
- unsigned long dirty_net_count;
- unsigned long dirty_block_count;
-} xc_shadow_control_stats_t;
-
+typedef dom0_getdomaininfo_t xc_domaininfo_t;
int xc_domain_create(int xc_handle,
unsigned int mem_kb,
- const char *name,
int cpu,
+ float cpu_weight,
u32 *pdomid);
+
+/**
+ * This function pauses a domain. A paused domain still exists in memory
+ * however it does not receive any timeslices from the hypervisor.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm domid the domain id to pause
+ * @return 0 on success, -1 on failure.
+ */
int xc_domain_pause(int xc_handle,
u32 domid);
+/**
+ * This function unpauses a domain. The domain should have been previously
+ * paused.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm domid the domain id to unpause
+ * return 0 on success, -1 on failure
+ */
int xc_domain_unpause(int xc_handle,
u32 domid);
+
+/**
+ * This function will destroy a domain. Destroying a domain removes the domain
+ * completely from memory. This function should be called after sending the
+ * domain a SHUTDOWN control message to free up the domain resources.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm domid the domain id to destroy
+ * @return 0 on success, -1 on failure
+ */
int xc_domain_destroy(int xc_handle,
u32 domid);
int xc_domain_pincpu(int xc_handle,
u32 domid,
int cpu);
+/**
+ * This function will return information about one or more domains.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm first_domid the first domain to enumerate information from. Domains
+ * are currently enumerate in order of creation.
+ * @parm max_doms the number of elements in info
+ * @parm info an array of max_doms size that will contain the information for
+ * the enumerated domains.
+ * @return the number of domains enumerated or -1 on error
+ */
int xc_domain_getinfo(int xc_handle,
u32 first_domid,
unsigned int max_doms,
xc_dominfo_t *info);
+/**
+ * This function returns information about one domain. This information is
+ * more detailed than the information from xc_domain_getinfo().
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm domid the domain to get information from
+ * @parm info a pointer to an xc_domaininfo_t to store the domain information
+ * @parm ctxt a pointer to a structure to store the execution context of the
+ * domain
+ * @return 0 on success, -1 on failure
+ */
+int xc_domain_getfullinfo(int xc_handle,
+ u32 domid,
+ u32 vcpu,
+ xc_domaininfo_t *info,
+ full_execution_context_t *ctxt);
+int xc_domain_setcpuweight(int xc_handle,
+ u32 domid,
+ float weight);
+long long xc_domain_get_cpu_usage(int xc_handle,
+ domid_t domid,
+ int vcpu);
+
+
+typedef dom0_shadow_control_stats_t xc_shadow_control_stats_t;
int xc_shadow_control(int xc_handle,
u32 domid,
unsigned int sop,
@@ -71,12 +162,33 @@ int xc_shadow_control(int xc_handle,
xc_shadow_control_stats_t *stats);
-#define XCFLAGS_VERBOSE 1
-#define XCFLAGS_LIVE 2
-#define XCFLAGS_DEBUG 4
+#define XCFLAGS_VERBOSE 1
+#define XCFLAGS_LIVE 2
+#define XCFLAGS_DEBUG 4
+#define XCFLAGS_CONFIGURE 8
struct XcIOContext;
+
+/**
+ * This function will save a domain running Linux to an IO context. This
+ * IO context is currently a private interface making this function difficult
+ * to call. It's interface will likely change in the future.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm ioctxt the IO context to save a domain to
+ * @return 0 on success, -1 on failure
+ */
int xc_linux_save(int xc_handle, struct XcIOContext *ioctxt);
+
+/**
+ * This function will restore a saved domain running Linux to an IO context.
+ * Like xc_linux_save(), this function uses a parameter who's structure is
+ * privately defined. It's interface will also likely change.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm ioctxt the IO context to restore a domain from
+ * @return 0 on success, -1 on failure
+ */
int xc_linux_restore(int xc_handle, struct XcIOContext *ioctxt);
int xc_linux_build(int xc_handle,
@@ -85,13 +197,27 @@ int xc_linux_build(int xc_handle,
const char *ramdisk_name,
const char *cmdline,
unsigned int control_evtchn,
- unsigned long flags);
-
-int xc_netbsd_build(int xc_handle,
- u32 domid,
- const char *image_name,
- const char *cmdline,
- unsigned int control_evtchn);
+ unsigned long flags,
+ unsigned int vcpus);
+
+int
+xc_plan9_build (int xc_handle,
+ u32 domid,
+ const char *image_name,
+ const char *cmdline,
+ unsigned int control_evtchn,
+ unsigned long flags);
+
+struct mem_map;
+int xc_vmx_build(int xc_handle,
+ u32 domid,
+ int memsize,
+ const char *image_name,
+ struct mem_map *memmap,
+ const char *ramdisk_name,
+ const char *cmdline,
+ unsigned int control_evtchn,
+ unsigned long flags);
int xc_bvtsched_global_set(int xc_handle,
unsigned long ctx_allow);
@@ -115,26 +241,6 @@ int xc_bvtsched_domain_get(int xc_handle,
long long *warpl,
long long *warpu);
-int xc_fbvtsched_global_set(int xc_handle,
- unsigned long ctx_allow);
-
-int xc_fbvtsched_domain_set(int xc_handle,
- u32 domid,
- unsigned long mcuadv,
- unsigned long warp,
- unsigned long warpl,
- unsigned long warpu);
-
-int xc_fbvtsched_global_get(int xc_handle,
- unsigned long *ctx_allow);
-
-int xc_fbvtsched_domain_get(int xc_handle,
- u32 domid,
- unsigned long *mcuadv,
- unsigned long *warp,
- unsigned long *warpl,
- unsigned long *warpu);
-
int xc_atropos_domain_set(int xc_handle,
u32 domid,
u64 period, u64 slice, u64 latency,
@@ -149,36 +255,76 @@ int xc_rrobin_global_set(int xc_handle, u64 slice);
int xc_rrobin_global_get(int xc_handle, u64 *slice);
-#define DOMID_SELF (0x7FFFFFFEU)
-
-typedef struct {
-#define EVTCHNSTAT_closed 0 /* Chennel is not in use. */
-#define EVTCHNSTAT_unbound 1 /* Channel is not bound to a source. */
-#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */
-#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */
-#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */
- int status;
- union {
- struct {
- u32 dom;
- int port;
- } interdomain;
- int pirq;
- int virq;
- } u;
-} xc_evtchn_status_t;
-
+typedef evtchn_status_t xc_evtchn_status_t;
+
+/*\
+ * EVENT CHANNEL FUNCTIONS
+\*/
+
+/**
+ * This function allocates an unbound port. Ports are named endpoints used for
+ * interdomain communication. This function is most useful in opening a
+ * well-known port within a domain to receive events on.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm dom the ID of the domain. This maybe DOMID_SELF
+ * @parm port a pointer to a port. This is an in/out parameter. If *port is
+ * 0, then a new port will be assigned, if port is > 0 then that
+ * port is allocated if the port is unallocated.
+ * @return 0 on success, -1 on failure
+ */
+int xc_evtchn_alloc_unbound(int xc_handle,
+ u32 dom,
+ int *port);
+
+/**
+ * This function creates a pair of ports between two domains. A port can only
+ * be bound once within a domain.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm dom1 one of the two domains to connect. Can be DOMID_SELF.
+ * @parm dom2 the other domain to connect. Can be DOMID_SELF.
+ * @parm port1 an in/out parameter. If > 0, then try to connect *port. If
+ * 0, then allocate a new port and store the port in *port.
+ * @parm port2 the port connected on port2. This parameter behaves the same
+ * way as port1.
+ * @return 0 on success, -1 on error.
+ */
int xc_evtchn_bind_interdomain(int xc_handle,
- u32 dom1, /* may be DOMID_SELF */
- u32 dom2, /* may be DOMID_SELF */
+ u32 dom1,
+ u32 dom2,
int *port1,
int *port2);
int xc_evtchn_bind_virq(int xc_handle,
int virq,
int *port);
+
+/**
+ * This function will close a single port on an event channel.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm dom the domain that the port exists on. May be DOMID_SELF.
+ * @parm port the port to close
+ * @return 0 on success, -1 on error
+ */
int xc_evtchn_close(int xc_handle,
u32 dom, /* may be DOMID_SELF */
int port);
+
+/**
+ * This function generates a notify event on a bound port.
+ *
+ * Notifies can be read within Linux by opening /dev/xen/evtchn and reading
+ * a 16 bit value. The result will be the port the event occurred on. When
+ * events occur, the port is masked until the 16 bit port value is written back
+ * to the file. When /dev/xen/evtchn is opened, it has to be bound via an
+ * ioctl to each port to listen on. The ioctl for binding is _IO('E', 2). The
+ * parameter is the port to listen on.
+ *
+ * @parm xc_handle a handle to an open hypervisor interface
+ * @parm local_port the port to generate the notify on
+ * @return 0 on success, -1 on error
+ */
int xc_evtchn_send(int xc_handle,
int local_port);
int xc_evtchn_status(int xc_handle,
@@ -198,28 +344,55 @@ int xc_readconsolering(int xc_handle,
unsigned int max_chars,
int clear);
-typedef struct {
- int ht_per_core;
- int cores;
- unsigned long total_pages;
- unsigned long free_pages;
- unsigned long cpu_khz;
-} xc_physinfo_t;
-
+typedef dom0_physinfo_t xc_physinfo_t;
int xc_physinfo(int xc_handle,
xc_physinfo_t *info);
-int xc_domain_setname(int xc_handle,
- u32 domid,
- char *name);
+int xc_sched_id(int xc_handle,
+ int *sched_id);
int xc_domain_setinitialmem(int xc_handle,
u32 domid,
unsigned int initial_memkb);
int xc_domain_setmaxmem(int xc_handle,
- u32 domid,
- unsigned int max_memkb);
+ u32 domid,
+ unsigned int max_memkb);
+
+int xc_domain_setvmassist(int xc_handle,
+ u32 domid,
+ unsigned int cmd,
+ unsigned int type);
+
+typedef dom0_perfc_desc_t xc_perfc_desc_t;
+/* IMPORTANT: The caller is responsible for mlock()'ing the @desc array. */
+int xc_perfc_control(int xc_handle,
+ u32 op,
+ xc_perfc_desc_t *desc);
+
+/**
+ * Memory maps a range within one domain to a local address range. Mappings
+ * should be unmapped with munmap and should follow the same rules as mmap
+ * regarding page alignment.
+ *
+ * In Linux, the ring queue for the control channel is accessible by mapping
+ * the shared_info_frame (from xc_domain_getinfo()) + 2048. The structure
+ * stored there is of type control_if_t.
+ *
+ * @parm xc_handle a handle on an open hypervisor interface
+ * @parm dom the domain to map memory from
+ * @parm size the amount of memory to map (in multiples of page size)
+ * @parm prot same flag as in mmap().
+ * @parm mfn the frame address to map.
+ */
+void *xc_map_foreign_range(int xc_handle, u32 dom,
+ int size, int prot,
+ unsigned long mfn );
+
+void *xc_map_foreign_batch(int xc_handle, u32 dom, int prot,
+ unsigned long *arr, int num );
+int xc_get_pfn_list(int xc_handle, u32 domid, unsigned long *pfn_buf,
+ unsigned long max_pfns);
#endif /* __XC_H__ */
diff --git a/tools/libxc/xc_domain.c b/tools/libxc/xc_domain.c
index d389757cee..7104ea1a48 100644
--- a/tools/libxc/xc_domain.c
+++ b/tools/libxc/xc_domain.c
@@ -10,8 +10,8 @@
int xc_domain_create(int xc_handle,
unsigned int mem_kb,
- const char *name,
int cpu,
+ float cpu_weight,
u32 *pdomid)
{
int err;
@@ -20,12 +20,14 @@ int xc_domain_create(int xc_handle,
op.cmd = DOM0_CREATEDOMAIN;
op.u.createdomain.domain = (domid_t)*pdomid;
op.u.createdomain.memory_kb = mem_kb;
- strncpy(op.u.createdomain.name, name, MAX_DOMAIN_NAME);
- op.u.createdomain.name[MAX_DOMAIN_NAME-1] = '\0';
op.u.createdomain.cpu = cpu;
if ( (err = do_dom0_op(xc_handle, &op)) == 0 )
- *pdomid = (u32)op.u.createdomain.domain;
+ {
+ *pdomid = (u16)op.u.createdomain.domain;
+
+ err = xc_domain_setcpuweight(xc_handle, *pdomid, cpu_weight);
+ }
return err;
}
@@ -67,6 +69,7 @@ int xc_domain_pincpu(int xc_handle,
dom0_op_t op;
op.cmd = DOM0_PINCPUDOMAIN;
op.u.pincpudomain.domain = (domid_t)domid;
+ op.u.pincpudomain.exec_domain = 0;
op.u.pincpudomain.cpu = cpu;
return do_dom0_op(xc_handle, &op);
}
@@ -85,10 +88,11 @@ int xc_domain_getinfo(int xc_handle,
{
op.cmd = DOM0_GETDOMAININFO;
op.u.getdomaininfo.domain = (domid_t)next_domid;
+ op.u.getdomaininfo.exec_domain = 0; // FIX ME?!?
op.u.getdomaininfo.ctxt = NULL; /* no exec context info, thanks. */
if ( do_dom0_op(xc_handle, &op) < 0 )
break;
- info->domid = (u32)op.u.getdomaininfo.domain;
+ info->domid = (u16)op.u.getdomaininfo.domain;
info->cpu =
(op.u.getdomaininfo.flags>>DOMFLAGS_CPUSHIFT) & DOMFLAGS_CPUMASK;
@@ -108,10 +112,8 @@ int xc_domain_getinfo(int xc_handle,
info->max_memkb = op.u.getdomaininfo.max_pages<<(PAGE_SHIFT);
info->shared_info_frame = op.u.getdomaininfo.shared_info_frame;
info->cpu_time = op.u.getdomaininfo.cpu_time;
- strncpy(info->name, op.u.getdomaininfo.name, XC_DOMINFO_MAXNAME);
- info->name[XC_DOMINFO_MAXNAME-1] = '\0';
- next_domid = (u32)op.u.getdomaininfo.domain + 1;
+ next_domid = (u16)op.u.getdomaininfo.domain + 1;
info++;
}
@@ -120,16 +122,23 @@ int xc_domain_getinfo(int xc_handle,
int xc_domain_getfullinfo(int xc_handle,
u32 domid,
- dom0_op_t *op,
- full_execution_context_t *ctxt )
+ u32 vcpu,
+ xc_domaininfo_t *info,
+ full_execution_context_t *ctxt)
{
int rc;
- op->cmd = DOM0_GETDOMAININFO;
- op->u.getdomaininfo.domain = (domid_t)domid;
- op->u.getdomaininfo.ctxt = ctxt;
+ dom0_op_t op;
+
+ op.cmd = DOM0_GETDOMAININFO;
+ op.u.getdomaininfo.domain = (domid_t)domid;
+ op.u.getdomaininfo.exec_domain = (u16)vcpu;
+ op.u.getdomaininfo.ctxt = ctxt;
+
+ rc = do_dom0_op(xc_handle, &op);
- rc = do_dom0_op(xc_handle, op);
- if ( ((u32)op->u.getdomaininfo.domain != domid) && rc > 0 )
+ memcpy(info, &op.u.getdomaininfo, sizeof(*info));
+
+ if ( ((u16)op.u.getdomaininfo.domain != domid) && rc > 0 )
return -ESRCH;
else
return rc;
@@ -160,17 +169,59 @@ int xc_shadow_control(int xc_handle,
return (rc == 0) ? op.u.shadow_control.pages : rc;
}
-int xc_domain_setname(int xc_handle,
- u32 domid,
- char *name)
+int xc_domain_setcpuweight(int xc_handle,
+ u32 domid,
+ float weight)
{
- dom0_op_t op;
- op.cmd = DOM0_SETDOMAINNAME;
- op.u.setdomainname.domain = (domid_t)domid;
- strncpy(op.u.setdomainname.name, name, MAX_DOMAIN_NAME);
- return do_dom0_op(xc_handle, &op);
+ int sched_id;
+ int ret;
+
+ /* Figure out which scheduler is currently used: */
+ if((ret = xc_sched_id(xc_handle, &sched_id)))
+ return ret;
+
+ switch(sched_id)
+ {
+ case SCHED_BVT:
+ {
+ u32 mcuadv;
+ int warpback;
+ s32 warpvalue;
+ long long warpl;
+ long long warpu;
+
+ /* Preserve all the scheduling parameters apart
+ of MCU advance. */
+ if((ret = xc_bvtsched_domain_get(xc_handle, domid, &mcuadv,
+ &warpback, &warpvalue, &warpl, &warpu)))
+ return ret;
+
+ /* The MCU advance is inverse of the weight.
+ Default value of the weight is 1, default mcuadv 10.
+ The scaling factor is therefore 10. */
+ if(weight > 0) mcuadv = 10 / weight;
+
+ ret = xc_bvtsched_domain_set(xc_handle, domid, mcuadv,
+ warpback, warpvalue, warpl, warpu);
+ break;
+ }
+
+ case SCHED_RROBIN:
+ {
+ /* The weight cannot be set for RRobin */
+ break;
+ }
+ case SCHED_ATROPOS:
+ {
+ /* TODO - can we set weights in Atropos? */
+ break;
+ }
+ }
+
+ return ret;
}
+
int xc_domain_setinitialmem(int xc_handle,
u32 domid,
unsigned int initial_memkb)
@@ -193,3 +244,15 @@ int xc_domain_setmaxmem(int xc_handle,
return do_dom0_op(xc_handle, &op);
}
+int xc_domain_setvmassist(int xc_handle,
+ u32 domid,
+ unsigned int cmd,
+ unsigned int type)
+{
+ dom0_op_t op;
+ op.cmd = DOM0_SETDOMAINVMASSIST;
+ op.u.setdomainvmassist.domain = (domid_t)domid;
+ op.u.setdomainvmassist.cmd = cmd;
+ op.u.setdomainvmassist.type = type;
+ return do_dom0_op(xc_handle, &op);
+}
diff --git a/tools/libxc/xc_evtchn.c b/tools/libxc/xc_evtchn.c
index 624f5b1c15..9371e61261 100644
--- a/tools/libxc/xc_evtchn.c
+++ b/tools/libxc/xc_evtchn.c
@@ -31,6 +31,26 @@ static int do_evtchn_op(int xc_handle, evtchn_op_t *op)
}
+int xc_evtchn_alloc_unbound(int xc_handle,
+ u32 dom,
+ int *port)
+{
+ evtchn_op_t op;
+ int rc;
+
+ op.cmd = EVTCHNOP_alloc_unbound;
+ op.u.alloc_unbound.dom = (domid_t)dom;
+
+ if ( (rc = do_evtchn_op(xc_handle, &op)) == 0 )
+ {
+ if ( port != NULL )
+ *port = op.u.alloc_unbound.port;
+ }
+
+ return rc;
+}
+
+
int xc_evtchn_bind_interdomain(int xc_handle,
u32 dom1,
u32 dom2,
@@ -41,9 +61,12 @@ int xc_evtchn_bind_interdomain(int xc_handle,
int rc;
op.cmd = EVTCHNOP_bind_interdomain;
- op.u.bind_interdomain.dom1 = (domid_t)dom1;
- op.u.bind_interdomain.dom2 = (domid_t)dom2;
-
+ op.u.bind_interdomain.dom1 = (domid_t)dom1;
+ op.u.bind_interdomain.dom2 = (domid_t)dom2;
+ op.u.bind_interdomain.port1 = (port1 != NULL) ? *port1 : 0;
+ op.u.bind_interdomain.port2 = (port2 != NULL) ? *port2 : 0;
+
+
if ( (rc = do_evtchn_op(xc_handle, &op)) == 0 )
{
if ( port1 != NULL )
@@ -111,21 +134,7 @@ int xc_evtchn_status(int xc_handle,
op.u.status.port = port;
if ( (rc = do_evtchn_op(xc_handle, &op)) == 0 )
- {
- switch ( status->status = op.u.status.status )
- {
- case EVTCHNSTAT_interdomain:
- status->u.interdomain.dom = (u32)op.u.status.u.interdomain.dom;
- status->u.interdomain.port = op.u.status.u.interdomain.port;
- break;
- case EVTCHNSTAT_pirq:
- status->u.pirq = op.u.status.u.pirq;
- break;
- case EVTCHNSTAT_virq:
- status->u.virq = op.u.status.u.virq;
- break;
- }
- }
+ memcpy(status, &op.u.status, sizeof(*status));
return rc;
}
diff --git a/tools/libxc/xc_fbvtsched.c b/tools/libxc/xc_fbvtsched.c
deleted file mode 100644
index 55adafe0d1..0000000000
--- a/tools/libxc/xc_fbvtsched.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/******************************************************************************
- * xc_fbvtsched.c
- *
- * API for manipulating parameters of the Fair Borrowed Virtual Time scheduler.
- *
- * Copyright (c) 2004, G. Milos
- * Based on K. Fraiser's xc_bvtsched.c
- */
-
-#include "xc_private.h"
-
-int xc_fbvtsched_global_set(int xc_handle,
- unsigned long ctx_allow)
-{
- dom0_op_t op;
-
- op.cmd = DOM0_SCHEDCTL;
- op.u.schedctl.sched_id = SCHED_FBVT;
- op.u.schedctl.direction = SCHED_INFO_PUT;
- op.u.schedctl.u.fbvt.ctx_allow = ctx_allow;
-
- return do_dom0_op(xc_handle, &op);
-}
-
-int xc_fbvtsched_global_get(int xc_handle,
- unsigned long *ctx_allow)
-{
- dom0_op_t op;
- int ret;
-
- op.cmd = DOM0_SCHEDCTL;
- op.u.schedctl.sched_id = SCHED_FBVT;
- op.u.schedctl.direction = SCHED_INFO_GET;
-
- ret = do_dom0_op(xc_handle, &op);
-
- *ctx_allow = op.u.schedctl.u.fbvt.ctx_allow;
-
- return ret;
-}
-
-int xc_fbvtsched_domain_set(int xc_handle,
- u32 domid,
- unsigned long mcuadv,
- unsigned long warp,
- unsigned long warpl,
- unsigned long warpu)
-{
- dom0_op_t op;
- struct fbvt_adjdom *fbvtadj = &op.u.adjustdom.u.fbvt;
-
- op.cmd = DOM0_ADJUSTDOM;
- op.u.adjustdom.domain = (domid_t)domid;
- op.u.adjustdom.sched_id = SCHED_FBVT;
- op.u.adjustdom.direction = SCHED_INFO_PUT;
-
- fbvtadj->mcu_adv = mcuadv;
- fbvtadj->warp = warp;
- fbvtadj->warpl = warpl;
- fbvtadj->warpu = warpu;
- return do_dom0_op(xc_handle, &op);
-}
-
-
-int xc_fbvtsched_domain_get(int xc_handle,
- u32 domid,
- unsigned long *mcuadv,
- unsigned long *warp,
- unsigned long *warpl,
- unsigned long *warpu)
-{
-
- dom0_op_t op;
- int ret;
- struct fbvt_adjdom *adjptr = &op.u.adjustdom.u.fbvt;
-
- op.cmd = DOM0_ADJUSTDOM;
- op.u.adjustdom.domain = (domid_t)domid;
- op.u.adjustdom.sched_id = SCHED_FBVT;
- op.u.adjustdom.direction = SCHED_INFO_GET;
-
- ret = do_dom0_op(xc_handle, &op);
-
- *mcuadv = adjptr->mcu_adv;
- *warp = adjptr->warp;
- *warpl = adjptr->warpl;
- *warpu = adjptr->warpu;
- return ret;
-}
diff --git a/tools/libxc/xc_io.c b/tools/libxc/xc_io.c
index 4615092e97..5589483f10 100644
--- a/tools/libxc/xc_io.c
+++ b/tools/libxc/xc_io.c
@@ -1,4 +1,13 @@
#include "xc_io.h"
+#include <sys/time.h>
+
+void xcio_timestamp(XcIOContext *ctxt, const char *msg){
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ if (msg[0] != '\b' && msg[0] != '\r')
+ fprintf(stdout, "[%08ld.%06ld] ", tv.tv_sec, tv.tv_usec);
+}
void xcio_error(XcIOContext *ctxt, const char *msg, ...){
va_list args;
@@ -15,8 +24,10 @@ void xcio_info(XcIOContext *ctxt, const char *msg, ...){
if(0 && !(ctxt->flags & XCFLAGS_VERBOSE)) return;
va_start(args, msg);
+ xcio_timestamp(ctxt, msg);
vfprintf(stdout, msg, args); fprintf(stdout, "\n");
IOStream_vprint(ctxt->info, msg, args);
+ fflush(stdout);
va_end(args);
}
@@ -25,6 +36,7 @@ void xcio_debug(XcIOContext *ctxt, const char *msg, ...){
if(0 && !(ctxt->flags & XCFLAGS_DEBUG)) return;
va_start(args, msg);
+ xcio_timestamp(ctxt, msg);
vfprintf(stdout, msg, args); fprintf(stdout, "\n");
IOStream_vprint(ctxt->info, msg, args);
va_end(args);
diff --git a/tools/libxc/xc_io.h b/tools/libxc/xc_io.h
index 00ed66dc24..341f29be60 100644
--- a/tools/libxc/xc_io.h
+++ b/tools/libxc/xc_io.h
@@ -8,12 +8,14 @@
typedef struct XcIOContext {
u32 domain;
unsigned flags;
+ int resource;
IOStream *io;
IOStream *info;
IOStream *err;
char *vmconfig;
int vmconfig_n;
- int (*suspend)(u32 domain, void *data);
+ int (*suspend)(void *data, u32 domain);
+ int (*configure)(void *data, u32 domain, char *vmconfig, int vmconfig_n);
void *data;
} XcIOContext;
@@ -21,7 +23,18 @@ static inline int xcio_suspend_domain(XcIOContext *ctxt){
int err = 0;
if(ctxt->suspend){
- err = ctxt->suspend(ctxt->domain, ctxt->data);
+ err = ctxt->suspend(ctxt->data, ctxt->domain);
+ } else {
+ err = -EINVAL;
+ }
+ return err;
+}
+
+static inline int xcio_configure_domain(XcIOContext *ctxt){
+ int err = 0;
+
+ if(ctxt->configure){
+ err = ctxt->configure(ctxt->data, ctxt->domain, ctxt->vmconfig, ctxt->vmconfig_n);
} else {
err = -EINVAL;
}
diff --git a/tools/libxc/xc_linux_build.c b/tools/libxc/xc_linux_build.c
index 23ec09cb09..12753b78a6 100644
--- a/tools/libxc/xc_linux_build.c
+++ b/tools/libxc/xc_linux_build.c
@@ -14,60 +14,34 @@
#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
#define round_pgdown(_p) ((_p)&PAGE_MASK)
-static int parseelfimage(char *elfbase,
- unsigned long elfsize,
- unsigned long *pvirtstart,
- unsigned long *pkernstart,
- unsigned long *pkernend,
- unsigned long *pkernentry);
-static int loadelfimage(char *elfbase, void *pmh, unsigned long *parray,
- unsigned long vstart);
-
-static long get_tot_pages(int xc_handle, u32 domid)
-{
- dom0_op_t op;
- op.cmd = DOM0_GETDOMAININFO;
- op.u.getdomaininfo.domain = (domid_t)domid;
- op.u.getdomaininfo.ctxt = NULL;
- return (do_dom0_op(xc_handle, &op) < 0) ?
- -1 : op.u.getdomaininfo.tot_pages;
-}
-
-static int get_pfn_list(int xc_handle,
- u32 domid,
- unsigned long *pfn_buf,
- unsigned long max_pfns)
+struct domain_setup_info
{
- dom0_op_t op;
- int ret;
- op.cmd = DOM0_GETMEMLIST;
- op.u.getmemlist.domain = (domid_t)domid;
- op.u.getmemlist.max_pfns = max_pfns;
- op.u.getmemlist.buffer = pfn_buf;
-
- if ( mlock(pfn_buf, max_pfns * sizeof(unsigned long)) != 0 )
- return -1;
-
- ret = do_dom0_op(xc_handle, &op);
-
- (void)munlock(pfn_buf, max_pfns * sizeof(unsigned long));
-
- return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
-}
-
-static int copy_to_domain_page(void *pm_handle,
- unsigned long dst_pfn,
- void *src_page)
-{
- void *vaddr = map_pfn_writeable(pm_handle, dst_pfn);
- if ( vaddr == NULL )
- return -1;
- memcpy(vaddr, src_page, PAGE_SIZE);
- unmap_pfn(pm_handle, vaddr);
- return 0;
-}
-
-static int setup_guestos(int xc_handle,
+ unsigned long v_start;
+ unsigned long v_end;
+ unsigned long v_kernstart;
+ unsigned long v_kernend;
+ unsigned long v_kernentry;
+
+ unsigned int use_writable_pagetables;
+ unsigned int load_bsd_symtab;
+
+ unsigned long symtab_addr;
+ unsigned long symtab_len;
+};
+
+static int
+parseelfimage(
+ char *elfbase, unsigned long elfsize, struct domain_setup_info *dsi);
+static int
+loadelfimage(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ unsigned long vstart);
+static int
+loadelfsymtab(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ struct domain_setup_info *dsi);
+
+static int setup_guest(int xc_handle,
u32 dom,
char *image, unsigned long image_size,
gzFile initrd_gfd, unsigned long initrd_len,
@@ -77,7 +51,8 @@ static int setup_guestos(int xc_handle,
const char *cmdline,
unsigned long shared_info_frame,
unsigned int control_evtchn,
- unsigned long flags)
+ unsigned long flags,
+ unsigned int vcpus)
{
l1_pgentry_t *vl1tab=NULL, *vl1e=NULL;
l2_pgentry_t *vl2tab=NULL, *vl2e=NULL;
@@ -85,20 +60,16 @@ static int setup_guestos(int xc_handle,
unsigned long l2tab;
unsigned long l1tab;
unsigned long count, i;
- extended_start_info_t *start_info;
+ start_info_t *start_info;
shared_info_t *shared_info;
mmu_t *mmu = NULL;
- void *pm_handle=NULL;
int rc;
unsigned long nr_pt_pages;
unsigned long ppt_alloc;
unsigned long *physmap, *physmap_e, physmap_pfn;
- unsigned long v_start;
- unsigned long vkern_start;
- unsigned long vkern_entry;
- unsigned long vkern_end;
+ struct domain_setup_info dsi;
unsigned long vinitrd_start;
unsigned long vinitrd_end;
unsigned long vphysmap_start;
@@ -111,12 +82,20 @@ static int setup_guestos(int xc_handle,
unsigned long vpt_end;
unsigned long v_end;
- rc = parseelfimage(image, image_size, &v_start,
- &vkern_start, &vkern_end, &vkern_entry);
+ memset(&dsi, 0, sizeof(struct domain_setup_info));
+
+ rc = parseelfimage(image, image_size, &dsi);
if ( rc != 0 )
goto error_out;
-
- if ( (v_start & (PAGE_SIZE-1)) != 0 )
+
+ if (dsi.use_writable_pagetables)
+ xc_domain_setvmassist(xc_handle, dom, VMASST_CMD_enable,
+ VMASST_TYPE_writable_pagetables);
+
+ if (dsi.load_bsd_symtab)
+ loadelfsymtab(image, xc_handle, dom, NULL, &dsi);
+
+ if ( (dsi.v_start & (PAGE_SIZE-1)) != 0 )
{
PERROR("Guest OS must load to a page boundary.\n");
goto error_out;
@@ -129,13 +108,13 @@ static int setup_guestos(int xc_handle,
* read-only). We have a pair of simultaneous equations in two unknowns,
* which we solve by exhaustive search.
*/
+ vinitrd_start = round_pgup(dsi.v_end);
+ vinitrd_end = vinitrd_start + initrd_len;
+ vphysmap_start = round_pgup(vinitrd_end);
+ vphysmap_end = vphysmap_start + (nr_pages * sizeof(unsigned long));
+ vpt_start = round_pgup(vphysmap_end);
for ( nr_pt_pages = 2; ; nr_pt_pages++ )
{
- vinitrd_start = round_pgup(vkern_end);
- vinitrd_end = vinitrd_start + initrd_len;
- vphysmap_start = round_pgup(vinitrd_end);
- vphysmap_end = vphysmap_start + (nr_pages * sizeof(unsigned long));
- vpt_start = round_pgup(vphysmap_end);
vpt_end = vpt_start + (nr_pt_pages * PAGE_SIZE);
vstartinfo_start = vpt_end;
vstartinfo_end = vstartinfo_start + PAGE_SIZE;
@@ -144,7 +123,7 @@ static int setup_guestos(int xc_handle,
v_end = (vstack_end + (1<<22)-1) & ~((1<<22)-1);
if ( (v_end - vstack_end) < (512 << 10) )
v_end += 1 << 22; /* Add extra 4MB to get >= 512kB padding. */
- if ( (((v_end - v_start + ((1<<L2_PAGETABLE_SHIFT)-1)) >>
+ if ( (((v_end - dsi.v_start + ((1<<L2_PAGETABLE_SHIFT)-1)) >>
L2_PAGETABLE_SHIFT) + 1) <= nr_pt_pages )
break;
}
@@ -157,45 +136,45 @@ static int setup_guestos(int xc_handle,
" Start info: %08lx->%08lx\n"
" Boot stack: %08lx->%08lx\n"
" TOTAL: %08lx->%08lx\n",
- vkern_start, vkern_end,
+ dsi.v_kernstart, dsi.v_kernend,
vinitrd_start, vinitrd_end,
vphysmap_start, vphysmap_end,
vpt_start, vpt_end,
vstartinfo_start, vstartinfo_end,
vstack_start, vstack_end,
- v_start, v_end);
- printf(" ENTRY ADDRESS: %08lx\n", vkern_entry);
+ dsi.v_start, v_end);
+ printf(" ENTRY ADDRESS: %08lx\n", dsi.v_kernentry);
- if ( (v_end - v_start) > (nr_pages * PAGE_SIZE) )
+ if ( (v_end - dsi.v_start) > (nr_pages * PAGE_SIZE) )
{
printf("Initial guest OS requires too much space\n"
"(%luMB is greater than %luMB limit)\n",
- (v_end-v_start)>>20, (nr_pages<<PAGE_SHIFT)>>20);
+ (v_end-dsi.v_start)>>20, (nr_pages<<PAGE_SHIFT)>>20);
goto error_out;
}
- if ( (pm_handle = init_pfn_mapper((domid_t)dom)) == NULL )
- goto error_out;
-
if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL )
{
PERROR("Could not allocate memory");
goto error_out;
}
- if ( get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages )
+ if ( xc_get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages )
{
PERROR("Could not get the page frame list");
goto error_out;
}
- loadelfimage(image, pm_handle, page_array, v_start);
+ loadelfimage(image, xc_handle, dom, page_array, dsi.v_start);
+
+ if (dsi.load_bsd_symtab)
+ loadelfsymtab(image, xc_handle, dom, page_array, &dsi);
/* Load the initial ramdisk image. */
if ( initrd_len != 0 )
{
- for ( i = (vinitrd_start - v_start);
- i < (vinitrd_end - v_start); i += PAGE_SIZE )
+ for ( i = (vinitrd_start - dsi.v_start);
+ i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE )
{
char page[PAGE_SIZE];
if ( gzread(initrd_gfd, page, PAGE_SIZE) == -1 )
@@ -203,7 +182,7 @@ static int setup_guestos(int xc_handle,
PERROR("Error reading initrd image, could not");
goto error_out;
}
- copy_to_domain_page(pm_handle,
+ xc_copy_to_domain_page(xc_handle, dom,
page_array[i>>PAGE_SHIFT], page);
}
}
@@ -212,58 +191,69 @@ static int setup_guestos(int xc_handle,
goto error_out;
/* First allocate page for page dir. */
- ppt_alloc = (vpt_start - v_start) >> PAGE_SHIFT;
+ ppt_alloc = (vpt_start - dsi.v_start) >> PAGE_SHIFT;
l2tab = page_array[ppt_alloc++] << PAGE_SHIFT;
ctxt->pt_base = l2tab;
/* Initialise the page tables. */
- if ( (vl2tab = map_pfn_writeable(pm_handle, l2tab >> PAGE_SHIFT)) == NULL )
+ if ( (vl2tab = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ l2tab >> PAGE_SHIFT)) == NULL )
goto error_out;
memset(vl2tab, 0, PAGE_SIZE);
- vl2e = &vl2tab[l2_table_offset(v_start)];
- for ( count = 0; count < ((v_end-v_start)>>PAGE_SHIFT); count++ )
+ vl2e = &vl2tab[l2_table_offset(dsi.v_start)];
+ for ( count = 0; count < ((v_end-dsi.v_start)>>PAGE_SHIFT); count++ )
{
if ( ((unsigned long)vl1e & (PAGE_SIZE-1)) == 0 )
{
l1tab = page_array[ppt_alloc++] << PAGE_SHIFT;
if ( vl1tab != NULL )
- unmap_pfn(pm_handle, vl1tab);
- if ( (vl1tab = map_pfn_writeable(pm_handle,
- l1tab >> PAGE_SHIFT)) == NULL )
+ munmap(vl1tab, PAGE_SIZE);
+ if ( (vl1tab = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ l1tab >> PAGE_SHIFT)) == NULL )
+ {
+ munmap(vl2tab, PAGE_SIZE);
goto error_out;
+ }
memset(vl1tab, 0, PAGE_SIZE);
- vl1e = &vl1tab[l1_table_offset(v_start + (count<<PAGE_SHIFT))];
+ vl1e = &vl1tab[l1_table_offset(dsi.v_start + (count<<PAGE_SHIFT))];
*vl2e++ = l1tab | L2_PROT;
}
*vl1e = (page_array[count] << PAGE_SHIFT) | L1_PROT;
- if ( (count >= ((vpt_start-v_start)>>PAGE_SHIFT)) &&
- (count < ((vpt_end -v_start)>>PAGE_SHIFT)) )
+ if ( (count >= ((vpt_start-dsi.v_start)>>PAGE_SHIFT)) &&
+ (count < ((vpt_end -dsi.v_start)>>PAGE_SHIFT)) )
*vl1e &= ~_PAGE_RW;
vl1e++;
}
- unmap_pfn(pm_handle, vl1tab);
- unmap_pfn(pm_handle, vl2tab);
+ munmap(vl1tab, PAGE_SIZE);
+ munmap(vl2tab, PAGE_SIZE);
/* Write the phys->machine and machine->phys table entries. */
- physmap_pfn = (vphysmap_start - v_start) >> PAGE_SHIFT;
- physmap = physmap_e =
- map_pfn_writeable(pm_handle, page_array[physmap_pfn++]);
+ physmap_pfn = (vphysmap_start - dsi.v_start) >> PAGE_SHIFT;
+ physmap = physmap_e = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE,
+ page_array[physmap_pfn++]);
for ( count = 0; count < nr_pages; count++ )
{
if ( add_mmu_update(xc_handle, mmu,
(page_array[count] << PAGE_SHIFT) |
MMU_MACHPHYS_UPDATE, count) )
+ {
+ munmap(physmap, PAGE_SIZE);
goto error_out;
+ }
*physmap_e++ = page_array[count];
if ( ((unsigned long)physmap_e & (PAGE_SIZE-1)) == 0 )
{
- unmap_pfn(pm_handle, physmap);
- physmap = physmap_e =
- map_pfn_writeable(pm_handle, page_array[physmap_pfn++]);
+ munmap(physmap, PAGE_SIZE);
+ physmap = physmap_e = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE,
+ page_array[physmap_pfn++]);
}
}
- unmap_pfn(pm_handle, physmap);
+ munmap(physmap, PAGE_SIZE);
/*
* Pin down l2tab addr as page dir page - causes hypervisor to provide
@@ -273,8 +263,9 @@ static int setup_guestos(int xc_handle,
l2tab | MMU_EXTENDED_COMMAND, MMUEXT_PIN_L2_TABLE) )
goto error_out;
- start_info = map_pfn_writeable(
- pm_handle, page_array[(vstartinfo_start-v_start)>>PAGE_SHIFT]);
+ start_info = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE,
+ page_array[(vstartinfo_start-dsi.v_start)>>PAGE_SHIFT]);
memset(start_info, 0, sizeof(*start_info));
start_info->nr_pages = nr_pages;
start_info->shared_info = shared_info_frame << PAGE_SHIFT;
@@ -290,109 +281,49 @@ static int setup_guestos(int xc_handle,
}
strncpy(start_info->cmd_line, cmdline, MAX_CMDLINE);
start_info->cmd_line[MAX_CMDLINE-1] = '\0';
- unmap_pfn(pm_handle, start_info);
+ munmap(start_info, PAGE_SIZE);
/* shared_info page starts its life empty. */
- shared_info = map_pfn_writeable(pm_handle, shared_info_frame);
+ shared_info = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, shared_info_frame);
memset(shared_info, 0, sizeof(shared_info_t));
/* Mask all upcalls... */
for ( i = 0; i < MAX_VIRT_CPUS; i++ )
shared_info->vcpu_data[i].evtchn_upcall_mask = 1;
- unmap_pfn(pm_handle, shared_info);
+
+ shared_info->n_vcpu = vcpus;
+ printf(" VCPUS: %d\n", shared_info->n_vcpu);
+
+ munmap(shared_info, PAGE_SIZE);
/* Send the page update requests down to the hypervisor. */
if ( finish_mmu_updates(xc_handle, mmu) )
goto error_out;
free(mmu);
- (void)close_pfn_mapper(pm_handle);
free(page_array);
*pvsi = vstartinfo_start;
- *pvke = vkern_entry;
+ *pvke = dsi.v_kernentry;
return 0;
error_out:
if ( mmu != NULL )
free(mmu);
- if ( pm_handle != NULL )
- (void)close_pfn_mapper(pm_handle);
if ( page_array != NULL )
free(page_array);
return -1;
}
-static unsigned long get_filesz(int fd)
-{
- u16 sig;
- u32 _sz = 0;
- unsigned long sz;
-
- lseek(fd, 0, SEEK_SET);
- read(fd, &sig, sizeof(sig));
- sz = lseek(fd, 0, SEEK_END);
- if ( sig == 0x8b1f ) /* GZIP signature? */
- {
- lseek(fd, -4, SEEK_END);
- read(fd, &_sz, 4);
- sz = _sz;
- }
- lseek(fd, 0, SEEK_SET);
-
- return sz;
-}
-
-static char *read_kernel_image(const char *filename, unsigned long *size)
-{
- int kernel_fd = -1;
- gzFile kernel_gfd = NULL;
- char *image = NULL;
- unsigned int bytes;
-
- if ( (kernel_fd = open(filename, O_RDONLY)) < 0 )
- {
- PERROR("Could not open kernel image");
- goto out;
- }
-
- *size = get_filesz(kernel_fd);
-
- if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL )
- {
- PERROR("Could not allocate decompression state for state file");
- goto out;
- }
-
- if ( (image = malloc(*size)) == NULL )
- {
- PERROR("Could not allocate memory for kernel image");
- goto out;
- }
-
- if ( (bytes = gzread(kernel_gfd, image, *size)) != *size )
- {
- PERROR("Error reading kernel image, could not"
- " read the whole image (%d != %ld).", bytes, *size);
- free(image);
- image = NULL;
- }
-
- out:
- if ( kernel_gfd != NULL )
- gzclose(kernel_gfd);
- else if ( kernel_fd >= 0 )
- close(kernel_fd);
- return image;
-}
-
int xc_linux_build(int xc_handle,
u32 domid,
const char *image_name,
const char *ramdisk_name,
const char *cmdline,
unsigned int control_evtchn,
- unsigned long flags)
+ unsigned long flags,
+ unsigned int vcpus)
{
dom0_op_t launch_op, op;
int initrd_fd = -1;
@@ -404,13 +335,13 @@ int xc_linux_build(int xc_handle,
unsigned long image_size, initrd_size=0;
unsigned long vstartinfo_start, vkern_entry;
- if ( (nr_pages = get_tot_pages(xc_handle, domid)) < 0 )
+ if ( (nr_pages = xc_get_tot_pages(xc_handle, domid)) < 0 )
{
PERROR("Could not find total pages for domain");
goto error_out;
}
- if ( (image = read_kernel_image(image_name, &image_size)) == NULL )
+ if ( (image = xc_read_kernel_image(image_name, &image_size)) == NULL )
goto error_out;
if ( (ramdisk_name != NULL) && (strlen(ramdisk_name) != 0) )
@@ -421,7 +352,7 @@ int xc_linux_build(int xc_handle,
goto error_out;
}
- initrd_size = get_filesz(initrd_fd);
+ initrd_size = xc_get_filesz(initrd_fd);
if ( (initrd_gfd = gzdopen(initrd_fd, "rb")) == NULL )
{
@@ -438,9 +369,10 @@ int xc_linux_build(int xc_handle,
op.cmd = DOM0_GETDOMAININFO;
op.u.getdomaininfo.domain = (domid_t)domid;
+ op.u.getdomaininfo.exec_domain = 0;
op.u.getdomaininfo.ctxt = ctxt;
if ( (do_dom0_op(xc_handle, &op) < 0) ||
- ((u32)op.u.getdomaininfo.domain != domid) )
+ ((u16)op.u.getdomaininfo.domain != domid) )
{
PERROR("Could not get info on domain");
goto error_out;
@@ -452,12 +384,12 @@ int xc_linux_build(int xc_handle,
goto error_out;
}
- if ( setup_guestos(xc_handle, domid, image, image_size,
+ if ( setup_guest(xc_handle, domid, image, image_size,
initrd_gfd, initrd_size, nr_pages,
&vstartinfo_start, &vkern_entry,
ctxt, cmdline,
op.u.getdomaininfo.shared_info_frame,
- control_evtchn, flags) < 0 )
+ control_evtchn, flags, vcpus) < 0 )
{
ERROR("Error constructing guest OS");
goto error_out;
@@ -474,21 +406,21 @@ int xc_linux_build(int xc_handle,
/*
* Initial register values:
- * DS,ES,FS,GS = FLAT_GUESTOS_DS
- * CS:EIP = FLAT_GUESTOS_CS:start_pc
- * SS:ESP = FLAT_GUESTOS_DS:start_stack
+ * DS,ES,FS,GS = FLAT_KERNEL_DS
+ * CS:EIP = FLAT_KERNEL_CS:start_pc
+ * SS:ESP = FLAT_KERNEL_DS:start_stack
* ESI = start_info
* [EAX,EBX,ECX,EDX,EDI,EBP are zero]
* EFLAGS = IF | 2 (bit 1 is reserved and should always be 1)
*/
- ctxt->cpu_ctxt.ds = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.es = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.fs = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.gs = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.ss = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.cs = FLAT_GUESTOS_CS;
+ ctxt->cpu_ctxt.ds = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.es = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.fs = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.gs = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.ss = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.cs = FLAT_KERNEL_CS;
ctxt->cpu_ctxt.eip = vkern_entry;
- ctxt->cpu_ctxt.esp = vstartinfo_start;
+ ctxt->cpu_ctxt.esp = vstartinfo_start + 2*PAGE_SIZE;
ctxt->cpu_ctxt.esi = vstartinfo_start;
ctxt->cpu_ctxt.eflags = (1<<9) | (1<<2);
@@ -499,7 +431,7 @@ int xc_linux_build(int xc_handle,
for ( i = 0; i < 256; i++ )
{
ctxt->trap_ctxt[i].vector = i;
- ctxt->trap_ctxt[i].cs = FLAT_GUESTOS_CS;
+ ctxt->trap_ctxt[i].cs = FLAT_KERNEL_CS;
}
ctxt->fast_trap_idx = 0;
@@ -510,16 +442,16 @@ int xc_linux_build(int xc_handle,
ctxt->gdt_ents = 0;
/* Ring 1 stack is the initial stack. */
- ctxt->guestos_ss = FLAT_GUESTOS_DS;
- ctxt->guestos_esp = vstartinfo_start;
+ ctxt->kernel_ss = FLAT_KERNEL_DS;
+ ctxt->kernel_esp = vstartinfo_start + 2*PAGE_SIZE;
/* No debugging. */
memset(ctxt->debugreg, 0, sizeof(ctxt->debugreg));
/* No callback handlers. */
- ctxt->event_callback_cs = FLAT_GUESTOS_CS;
+ ctxt->event_callback_cs = FLAT_KERNEL_CS;
ctxt->event_callback_eip = 0;
- ctxt->failsafe_callback_cs = FLAT_GUESTOS_CS;
+ ctxt->failsafe_callback_cs = FLAT_KERNEL_CS;
ctxt->failsafe_callback_eip = 0;
memset( &launch_op, 0, sizeof(launch_op) );
@@ -551,10 +483,7 @@ static inline int is_loadable_phdr(Elf_Phdr *phdr)
static int parseelfimage(char *elfbase,
unsigned long elfsize,
- unsigned long *pvirtstart,
- unsigned long *pkernstart,
- unsigned long *pkernend,
- unsigned long *pkernentry)
+ struct domain_setup_info *dsi)
{
Elf_Ehdr *ehdr = (Elf_Ehdr *)elfbase;
Elf_Phdr *phdr;
@@ -600,10 +529,18 @@ static int parseelfimage(char *elfbase,
guestinfo = elfbase + shdr->sh_offset;
- if ( (strstr(guestinfo, "GUEST_OS=linux") == NULL) ||
- (strstr(guestinfo, "XEN_VER=1.3") == NULL) )
+ if ( (strstr(guestinfo, "LOADER=generic") == NULL) &&
+ (strstr(guestinfo, "GUEST_OS=linux") == NULL) )
{
- ERROR("Will only load Linux images built for Xen v1.3");
+ ERROR("Will only load images built for the generic loader "
+ "or Linux images");
+ ERROR("Actually saw: '%s'", guestinfo);
+ return -EINVAL;
+ }
+
+ if ( (strstr(guestinfo, "XEN_VER=2.0") == NULL) )
+ {
+ ERROR("Will only load images built for Xen v2.0");
ERROR("Actually saw: '%s'", guestinfo);
return -EINVAL;
}
@@ -621,10 +558,10 @@ static int parseelfimage(char *elfbase,
phdr = (Elf_Phdr *)(elfbase + ehdr->e_phoff + (h*ehdr->e_phentsize));
if ( !is_loadable_phdr(phdr) )
continue;
- if ( phdr->p_vaddr < kernstart )
- kernstart = phdr->p_vaddr;
- if ( (phdr->p_vaddr + phdr->p_memsz) > kernend )
- kernend = phdr->p_vaddr + phdr->p_memsz;
+ if ( phdr->p_paddr < kernstart )
+ kernstart = phdr->p_paddr;
+ if ( (phdr->p_paddr + phdr->p_memsz) > kernend )
+ kernend = phdr->p_paddr + phdr->p_memsz;
}
if ( (kernstart > kernend) ||
@@ -635,19 +572,29 @@ static int parseelfimage(char *elfbase,
return -EINVAL;
}
- *pvirtstart = kernstart;
+ dsi->v_start = kernstart;
if ( (p = strstr(guestinfo, "VIRT_BASE=")) != NULL )
- *pvirtstart = strtoul(p+10, &p, 0);
+ dsi->v_start = strtoul(p+10, &p, 0);
+
+ if ( (p = strstr(guestinfo, "PT_MODE_WRITABLE")) != NULL )
+ dsi->use_writable_pagetables = 1;
- *pkernstart = kernstart;
- *pkernend = kernend;
- *pkernentry = ehdr->e_entry;
+ if ( (p = strstr(guestinfo, "BSD_SYMTAB")) != NULL )
+ dsi->load_bsd_symtab = 1;
+
+ dsi->v_kernstart = kernstart;
+ dsi->v_kernend = kernend;
+ dsi->v_kernentry = ehdr->e_entry;
+
+ dsi->v_end = dsi->v_kernend;
return 0;
}
-static int loadelfimage(char *elfbase, void *pmh, unsigned long *parray,
- unsigned long vstart)
+static int
+loadelfimage(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ unsigned long vstart)
{
Elf_Ehdr *ehdr = (Elf_Ehdr *)elfbase;
Elf_Phdr *phdr;
@@ -664,28 +611,126 @@ static int loadelfimage(char *elfbase, void *pmh, unsigned long *parray,
for ( done = 0; done < phdr->p_filesz; done += chunksz )
{
- pa = (phdr->p_vaddr + done) - vstart;
- va = map_pfn_writeable(pmh, parray[pa>>PAGE_SHIFT]);
+ pa = (phdr->p_paddr + done) - vstart;
+ va = xc_map_foreign_range(
+ xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
chunksz = phdr->p_filesz - done;
if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
memcpy(va + (pa & (PAGE_SIZE-1)),
elfbase + phdr->p_offset + done, chunksz);
- unmap_pfn(pmh, va);
+ munmap(va, PAGE_SIZE);
}
for ( ; done < phdr->p_memsz; done += chunksz )
{
- pa = (phdr->p_vaddr + done) - vstart;
- va = map_pfn_writeable(pmh, parray[pa>>PAGE_SHIFT]);
+ pa = (phdr->p_paddr + done) - vstart;
+ va = xc_map_foreign_range(
+ xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
chunksz = phdr->p_memsz - done;
if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
memset(va + (pa & (PAGE_SIZE-1)), 0, chunksz);
- unmap_pfn(pmh, va);
+ munmap(va, PAGE_SIZE);
}
}
return 0;
}
+#define ELFROUND (ELFSIZE / 8)
+
+static int
+loadelfsymtab(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ struct domain_setup_info *dsi)
+{
+ Elf_Ehdr *ehdr = (Elf_Ehdr *)elfbase, *sym_ehdr;
+ Elf_Shdr *shdr;
+ unsigned long maxva, symva;
+ char *p;
+ int h, i;
+
+ p = malloc(sizeof(int) + sizeof(Elf_Ehdr) +
+ ehdr->e_shnum * sizeof(Elf_Shdr));
+ if (p == NULL)
+ return 0;
+
+ maxva = (dsi->v_kernend + ELFROUND - 1) & ~(ELFROUND - 1);
+ symva = maxva;
+ maxva += sizeof(int);
+ dsi->symtab_addr = maxva;
+ dsi->symtab_len = 0;
+ maxva += sizeof(Elf_Ehdr) + ehdr->e_shnum * sizeof(Elf_Shdr);
+ maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
+
+ shdr = (Elf_Shdr *)(p + sizeof(int) + sizeof(Elf_Ehdr));
+ memcpy(shdr, elfbase + ehdr->e_shoff, ehdr->e_shnum * sizeof(Elf_Shdr));
+
+ for ( h = 0; h < ehdr->e_shnum; h++ )
+ {
+ if ( shdr[h].sh_type == SHT_STRTAB )
+ {
+ /* Look for a strtab @i linked to symtab @h. */
+ for ( i = 0; i < ehdr->e_shnum; i++ )
+ if ( (shdr[i].sh_type == SHT_SYMTAB) &&
+ (shdr[i].sh_link == h) )
+ break;
+ /* Skip symtab @h if we found no corresponding strtab @i. */
+ if ( i == ehdr->e_shnum )
+ {
+ shdr[h].sh_offset = 0;
+ continue;
+ }
+ }
+
+ if ( (shdr[h].sh_type == SHT_STRTAB) ||
+ (shdr[h].sh_type == SHT_SYMTAB) )
+ {
+ if ( parray != NULL )
+ xc_map_memcpy(maxva, elfbase + shdr[h].sh_offset, shdr[h].sh_size,
+ xch, dom, parray, dsi->v_start);
+
+ /* Mangled to be based on ELF header location. */
+ shdr[h].sh_offset = maxva - dsi->symtab_addr;
+
+ dsi->symtab_len += shdr[h].sh_size;
+ maxva += shdr[h].sh_size;
+ maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
+ }
+
+ shdr[h].sh_name = 0; /* Name is NULL. */
+ }
+
+ if ( dsi->symtab_len == 0 )
+ {
+ dsi->symtab_addr = 0;
+ goto out;
+ }
+
+ if ( parray != NULL )
+ {
+ *(int *)p = maxva - dsi->symtab_addr;
+ sym_ehdr = (Elf_Ehdr *)(p + sizeof(int));
+ memcpy(sym_ehdr, ehdr, sizeof(Elf_Ehdr));
+ sym_ehdr->e_phoff = 0;
+ sym_ehdr->e_shoff = sizeof(Elf_Ehdr);
+ sym_ehdr->e_phentsize = 0;
+ sym_ehdr->e_phnum = 0;
+ sym_ehdr->e_shstrndx = SHN_UNDEF;
+
+ /* Copy total length, crafted ELF header and section header table */
+ xc_map_memcpy(symva, p, sizeof(int) + sizeof(Elf_Ehdr) +
+ ehdr->e_shnum * sizeof(Elf_Shdr), xch, dom, parray,
+ dsi->v_start);
+ }
+
+ dsi->symtab_len = maxva - dsi->symtab_addr;
+ dsi->v_end = round_pgup(maxva);
+
+ out:
+ if ( p != NULL )
+ free(p);
+
+ return 0;
+}
diff --git a/tools/libxc/xc_linux_restore.c b/tools/libxc/xc_linux_restore.c
index f091914dba..b381bf0638 100644
--- a/tools/libxc/xc_linux_restore.c
+++ b/tools/libxc/xc_linux_restore.c
@@ -7,7 +7,7 @@
*/
#include "xc_private.h"
-#include <asm-xen/suspend.h>
+#include <xen/linux/suspend.h>
#define MAX_BATCH_SIZE 1024
@@ -19,31 +19,6 @@
#define DPRINTF(_f, _a...) ((void)0)
#endif
-static int get_pfn_list(int xc_handle,
- u32 domain_id,
- unsigned long *pfn_buf,
- unsigned long max_pfns)
-{
- dom0_op_t op;
- int ret;
- op.cmd = DOM0_GETMEMLIST;
- op.u.getmemlist.domain = (domid_t)domain_id;
- op.u.getmemlist.max_pfns = max_pfns;
- op.u.getmemlist.buffer = pfn_buf;
-
- if ( mlock(pfn_buf, max_pfns * sizeof(unsigned long)) != 0 )
- {
- PERROR("Could not lock pfn list buffer");
- return -1;
- }
-
- ret = do_dom0_op(xc_handle, &op);
-
- (void)munlock(pfn_buf, max_pfns * sizeof(unsigned long));
-
- return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
-}
-
/** Read the vmconfig string from the state input.
* It is stored as a 4-byte count 'n' followed by n bytes.
* The config data is stored in a new string in 'ioctxt->vmconfig',
@@ -86,7 +61,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
int rc = 1, i, n, k;
unsigned long mfn, pfn, xpfn;
unsigned int prev_pc, this_pc;
- u32 dom = ioctxt->domain;
+ u32 dom = 0;
int verify = 0;
/* Number of page frames in use by this Linux session. */
@@ -94,7 +69,8 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
/* The new domain's shared-info frame number. */
unsigned long shared_info_frame;
- unsigned char shared_info[PAGE_SIZE]; /* saved contents from file */
+ unsigned char shared_info_page[PAGE_SIZE]; /* saved contents from file */
+ shared_info_t *shared_info = (shared_info_t *)shared_info_page;
/* A copy of the CPU context of the guest. */
full_execution_context_t ctxt;
@@ -102,9 +78,6 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
/* First 16 bytes of the state file must contain 'LinuxGuestRecord'. */
char signature[16];
- /* A copy of the domain's name. */
- char name[MAX_DOMAIN_NAME];
-
/* A table containg the type of each PFN (/not/ MFN!). */
unsigned long *pfn_type = NULL;
@@ -112,7 +85,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
unsigned long *region_mfn = NULL;
/* A temporary mapping, and a copy, of one frame of guest memory. */
- unsigned long *ppage;
+ unsigned long *ppage = NULL;
/* A copy of the pfn-to-mfn table frame list. */
unsigned long pfn_to_mfn_frame_list[1024];
@@ -130,11 +103,11 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
mmu_t *mmu = NULL;
- void *pm_handle = NULL;
-
/* used by debug verify code */
unsigned long buf[PAGE_SIZE/sizeof(unsigned long)];
+ xcio_info(ioctxt, "xc_linux_restore start\n");
+
if ( mlock(&ctxt, sizeof(ctxt) ) )
{
/* needed for when we do the build dom0 op,
@@ -143,7 +116,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
return 1;
}
- /* Start writing out the saved-domain record. */
+ /* Start reading the saved-domain record. */
if ( xcio_read(ioctxt, signature, 16) ||
(memcmp(signature, "LinuxGuestRecord", 16) != 0) )
{
@@ -151,8 +124,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
goto out;
}
- if ( xcio_read(ioctxt, name, sizeof(name)) ||
- xcio_read(ioctxt, &nr_pfns, sizeof(unsigned long)) ||
+ if ( xcio_read(ioctxt, &nr_pfns, sizeof(unsigned long)) ||
xcio_read(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) )
{
xcio_error(ioctxt, "Error reading header");
@@ -165,17 +137,6 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
goto out;
}
- for ( i = 0; i < MAX_DOMAIN_NAME; i++ )
- {
- if ( name[i] == '\0' ) break;
- if ( name[i] & 0x80 )
- {
- xcio_error(ioctxt, "Random characters in domain name");
- goto out;
- }
- }
- name[MAX_DOMAIN_NAME-1] = '\0';
-
if ( nr_pfns > 1024*1024 )
{
xcio_error(ioctxt, "Invalid state file -- pfn count out of range");
@@ -201,26 +162,22 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
goto out;
}
- /* Set the domain's name to that from the restore file */
- if ( xc_domain_setname( xc_handle, dom, name ) )
+ /* Create domain on CPU -1 so that it may auto load-balance in future. */
+ if ( xc_domain_create(xc_handle, nr_pfns * (PAGE_SIZE / 1024),
+ -1, 1, &dom) )
{
- xcio_error(ioctxt, "Could not set domain name");
- goto out;
- }
-
- /* Set the domain's initial memory allocation
- to that from the restore file */
-
- if ( xc_domain_setinitialmem(xc_handle, dom,
- nr_pfns * (PAGE_SIZE / 1024)) )
- {
- xcio_error(ioctxt, "Could not set domain initial memory");
+ xcio_error(ioctxt, "Could not create domain. pfns=%d, %dKB",
+ nr_pfns,nr_pfns * (PAGE_SIZE / 1024));
goto out;
}
+
+ ioctxt->domain = dom;
+ xcio_info(ioctxt, "Created domain %ld\n",dom);
/* Get the domain's shared-info frame. */
op.cmd = DOM0_GETDOMAININFO;
op.u.getdomaininfo.domain = (domid_t)dom;
+ op.u.getdomaininfo.exec_domain = 0;
op.u.getdomaininfo.ctxt = NULL;
if ( do_dom0_op(xc_handle, &op) < 0 )
{
@@ -229,11 +186,17 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
}
shared_info_frame = op.u.getdomaininfo.shared_info_frame;
- if ( (pm_handle = init_pfn_mapper((domid_t)dom)) == NULL )
- goto out;
+ if(ioctxt->flags & XCFLAGS_CONFIGURE)
+ {
+ if(xcio_configure_domain(ioctxt))
+ {
+ xcio_error(ioctxt, "Configuring domain failed");
+ goto out;
+ }
+ }
/* Build the pfn-to-mfn table. We choose MFN ordering returned by Xen. */
- if ( get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns )
+ if ( xc_get_pfn_list(xc_handle, dom, pfn_to_mfn_table, nr_pfns) != nr_pfns )
{
xcio_error(ioctxt, "Did not read correct number of frame "
"numbers for new dom");
@@ -309,7 +272,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
}
}
- if ( (region_base = mfn_mapper_map_batch( xc_handle, dom,
+ if ( (region_base = xc_map_foreign_batch( xc_handle, dom,
PROT_WRITE,
region_mfn,
j )) == 0 )
@@ -349,7 +312,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
goto out;
}
- switch( region_pfn_type[i] )
+ switch( region_pfn_type[i] & LTABTYPE_MASK )
{
case 0:
break;
@@ -449,7 +412,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
n+=j; /* crude stats */
}
- DPRINTF("Received all pages\n");
+ xcio_info(ioctxt, "Received all pages\n");
/*
* Pin page tables. Do this after writing to them as otherwise Xen
@@ -457,7 +420,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
*/
for ( i = 0; i < nr_pfns; i++ )
{
- if ( pfn_type[i] == L1TAB )
+ if ( pfn_type[i] == (L1TAB|LPINTAB) )
{
if ( add_mmu_update(xc_handle, mmu,
(pfn_to_mfn_table[i]<<PAGE_SHIFT) |
@@ -468,7 +431,12 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
goto out;
}
}
- else if ( pfn_type[i] == L2TAB )
+ }
+
+ /* must pin all L1's before L2's (need consistent va back ptr) */
+ for ( i = 0; i < nr_pfns; i++ )
+ {
+ if ( pfn_type[i] == (L2TAB|LPINTAB) )
{
if ( add_mmu_update(xc_handle, mmu,
(pfn_to_mfn_table[i]<<PAGE_SHIFT) |
@@ -484,11 +452,58 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
if ( finish_mmu_updates(xc_handle, mmu) ) goto out;
- xcio_info(ioctxt, "\b\b\b\b100%%\nMemory reloaded.\n");
+ xcio_info(ioctxt, "\b\b\b\b100%%\n");
+ xcio_info(ioctxt, "Memory reloaded.\n");
+ /* Get the list of PFNs that are not in the psuedo-phys map */
+ {
+ unsigned int count, *pfntab;
+ int rc;
+
+ if ( xcio_read(ioctxt, &count, sizeof(count)) )
+ {
+ xcio_error(ioctxt, "Error when reading from state file");
+ goto out;
+ }
+
+ pfntab = malloc( sizeof(unsigned int) * count );
+ if ( pfntab == NULL )
+ {
+ xcio_error(ioctxt, "Out of memory");
+ goto out;
+ }
+
+ if ( xcio_read(ioctxt, pfntab, sizeof(unsigned int)*count) )
+ {
+ xcio_error(ioctxt, "Error when reading pfntab from state file");
+ goto out;
+ }
+
+ for ( i = 0; i < count; i++ )
+ {
+ unsigned long pfn = pfntab[i];
+ pfntab[i]=pfn_to_mfn_table[pfn];
+ pfn_to_mfn_table[pfn] = 0x80000001; // not in pmap
+ }
+
+ if ( count > 0 )
+ {
+ if ( (rc = do_dom_mem_op( xc_handle,
+ MEMOP_decrease_reservation,
+ pfntab, count, 0, dom )) <0 )
+ {
+ xcio_error(ioctxt, "Could not decrease reservation : %d",rc);
+ goto out;
+ }
+ else
+ {
+ printf("Decreased reservation by %d pages\n", count);
+ }
+ }
+ }
- if ( xcio_read(ioctxt, &ctxt, sizeof(ctxt)) ||
- xcio_read(ioctxt, shared_info, PAGE_SIZE) )
+ if ( xcio_read(ioctxt, &ctxt, sizeof(ctxt)) ||
+ xcio_read(ioctxt, shared_info_page, PAGE_SIZE) )
{
xcio_error(ioctxt, "Error when reading from state file");
goto out;
@@ -502,11 +517,12 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
goto out;
}
ctxt.cpu_ctxt.esi = mfn = pfn_to_mfn_table[pfn];
- p_srec = map_pfn_writeable(pm_handle, mfn);
+ p_srec = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_WRITE, mfn);
p_srec->resume_info.nr_pages = nr_pfns;
p_srec->resume_info.shared_info = shared_info_frame << PAGE_SHIFT;
p_srec->resume_info.flags = 0;
- unmap_pfn(pm_handle, p_srec);
+ munmap(p_srec, PAGE_SIZE);
/* Uncanonicalise each GDT frame number. */
if ( ctxt.gdt_ents > 8192 )
@@ -514,6 +530,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
xcio_error(ioctxt, "GDT entry count out of range");
goto out;
}
+
for ( i = 0; i < ctxt.gdt_ents; i += 512 )
{
pfn = ctxt.gdt_frames[i];
@@ -527,7 +544,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
/* Uncanonicalise the page table base pointer. */
pfn = ctxt.pt_base >> PAGE_SHIFT;
- if ( (pfn >= nr_pfns) || (pfn_type[pfn] != L2TAB) )
+ if ( (pfn >= nr_pfns) || ((pfn_type[pfn]&LTABTYPE_MASK) != L2TAB) )
{
printf("PT base is bad. pfn=%lu nr=%lu type=%08lx %08lx\n",
pfn, nr_pfns, pfn_type[pfn], (unsigned long)L2TAB);
@@ -536,17 +553,17 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
}
ctxt.pt_base = pfn_to_mfn_table[pfn] << PAGE_SHIFT;
-
/* clear any pending events and the selector */
- memset( &(((shared_info_t *)shared_info)->evtchn_pending[0]),
- 0, sizeof (((shared_info_t *)shared_info)->evtchn_pending)+
- sizeof(((shared_info_t *)shared_info)->evtchn_pending_sel) );
+ memset(&(shared_info->evtchn_pending[0]), 0,
+ sizeof (shared_info->evtchn_pending));
+ for ( i = 0; i < MAX_VIRT_CPUS; i++ )
+ shared_info->vcpu_data[i].evtchn_pending_sel = 0;
/* Copy saved contents of shared-info page. No checking needed. */
- ppage = map_pfn_writeable(pm_handle, shared_info_frame);
+ ppage = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_WRITE, shared_info_frame);
memcpy(ppage, shared_info, sizeof(shared_info_t));
- unmap_pfn(pm_handle, ppage);
-
+ munmap(ppage, PAGE_SIZE);
/* Uncanonicalise the pfn-to-mfn table frame-number list. */
for ( i = 0; i < (nr_pfns+1023)/1024; i++ )
@@ -564,7 +581,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
}
if ( (live_pfn_to_mfn_table =
- mfn_mapper_map_batch(xc_handle, dom,
+ xc_map_foreign_batch(xc_handle, dom,
PROT_WRITE,
pfn_to_mfn_frame_list,
(nr_pfns+1023)/1024 )) == 0 )
@@ -586,7 +603,7 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
* 4. fast_trap_idx is checked by Xen.
* 5. ldt base must be page-aligned, no more than 8192 ents, ...
* 6. gdt already done, and further checking is done by Xen.
- * 7. check that guestos_ss is safe.
+ * 7. check that kernel_ss is safe.
* 8. pt_base is already done.
* 9. debugregs are checked by Xen.
* 10. callback code selectors need checking.
@@ -595,14 +612,14 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
{
ctxt.trap_ctxt[i].vector = i;
if ( (ctxt.trap_ctxt[i].cs & 3) == 0 )
- ctxt.trap_ctxt[i].cs = FLAT_GUESTOS_CS;
+ ctxt.trap_ctxt[i].cs = FLAT_KERNEL_CS;
}
- if ( (ctxt.guestos_ss & 3) == 0 )
- ctxt.guestos_ss = FLAT_GUESTOS_DS;
+ if ( (ctxt.kernel_ss & 3) == 0 )
+ ctxt.kernel_ss = FLAT_KERNEL_DS;
if ( (ctxt.event_callback_cs & 3) == 0 )
- ctxt.event_callback_cs = FLAT_GUESTOS_CS;
+ ctxt.event_callback_cs = FLAT_KERNEL_CS;
if ( (ctxt.failsafe_callback_cs & 3) == 0 )
- ctxt.failsafe_callback_cs = FLAT_GUESTOS_CS;
+ ctxt.failsafe_callback_cs = FLAT_KERNEL_CS;
if ( ((ctxt.ldt_base & (PAGE_SIZE - 1)) != 0) ||
(ctxt.ldt_ents > 8192) ||
(ctxt.ldt_base > HYPERVISOR_VIRT_START) ||
@@ -611,14 +628,28 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
xcio_error(ioctxt, "Bad LDT base or size");
goto out;
}
-
+
+ xcio_info(ioctxt, "Domain ready to be built.\n");
+
op.cmd = DOM0_BUILDDOMAIN;
op.u.builddomain.domain = (domid_t)dom;
op.u.builddomain.ctxt = &ctxt;
rc = do_dom0_op(xc_handle, &op);
- /* don't start the domain as we have console etc to set up */
-
+ if ( rc != 0 )
+ {
+ xcio_error(ioctxt, "Couldn't build the domain");
+ goto out;
+ }
+
+ if ( ioctxt->flags & XCFLAGS_CONFIGURE )
+ {
+ xcio_info(ioctxt, "Domain ready to be unpaused\n");
+ op.cmd = DOM0_UNPAUSEDOMAIN;
+ op.u.unpausedomain.domain = (domid_t)dom;
+ rc = do_dom0_op(xc_handle, &op);
+ }
+
if ( rc == 0 )
{
/* Success: print the domain id. */
@@ -632,8 +663,6 @@ int xc_linux_restore(int xc_handle, XcIOContext *ioctxt)
xc_domain_destroy(xc_handle, dom);
if ( mmu != NULL )
free(mmu);
- if ( pm_handle != NULL )
- (void)close_pfn_mapper(pm_handle);
if ( pfn_to_mfn_table != NULL )
free(pfn_to_mfn_table);
if ( pfn_type != NULL )
diff --git a/tools/libxc/xc_linux_save.c b/tools/libxc/xc_linux_save.c
index c74e209bdc..9d07efe158 100644
--- a/tools/libxc/xc_linux_save.c
+++ b/tools/libxc/xc_linux_save.c
@@ -6,13 +6,18 @@
* Copyright (c) 2003, K A Fraser.
*/
+#include <inttypes.h>
#include <sys/time.h>
#include "xc_private.h"
-#include <asm-xen/suspend.h>
+#include <xen/linux/suspend.h>
+#include <xen/io/domain_controller.h>
+#include <time.h>
#define BATCH_SIZE 1024 /* 1024 pages (4MB) at a time */
-#define DEBUG 0
+#define MAX_MBIT_RATE 500
+
+#define DEBUG 0
#define DDEBUG 0
#if DEBUG
@@ -30,16 +35,14 @@
/*
* Returns TRUE if the given machine frame number has a unique mapping
* in the guest's pseudophysical map.
- * 0x80000000-3 mark the shared_info, and blk/net rings
*/
+
#define MFN_IS_IN_PSEUDOPHYS_MAP(_mfn) \
(((_mfn) < (1024*1024)) && \
- (((live_mfn_to_pfn_table[_mfn] < nr_pfns) && \
- (live_pfn_to_mfn_table[live_mfn_to_pfn_table[_mfn]] == (_mfn))) || \
- ((live_mfn_to_pfn_table[_mfn] >= 0x80000000) && \
- (live_mfn_to_pfn_table[_mfn] <= 0x80000003)) || \
- (live_pfn_to_mfn_table[live_mfn_to_pfn_table[_mfn]] == 0x80000004)))
-
+ ((live_mfn_to_pfn_table[_mfn] < nr_pfns) && \
+ (live_pfn_to_mfn_table[live_mfn_to_pfn_table[_mfn]] == (_mfn))))
+
+
/* Returns TRUE if MFN is successfully converted to a PFN. */
#define translate_mfn_to_pfn(_pmfn) \
({ \
@@ -52,6 +55,8 @@
_res; \
})
+#define is_mapped(pfn) (!((pfn) & 0x80000000UL))
+
static inline int test_bit ( int nr, volatile void * addr)
{
return (((unsigned long*)addr)[nr/(sizeof(unsigned long)*8)] >>
@@ -138,6 +143,80 @@ static long long tv_delta( struct timeval *new, struct timeval *old )
(new->tv_usec - old->tv_usec);
}
+
+#define START_MBIT_RATE ioctxt->resource
+
+static int mbit_rate, ombit_rate = 0;
+static int burst_time_us = -1;
+
+#define MBIT_RATE mbit_rate
+#define BURST_BUDGET (100*1024)
+
+/*
+ 1000000/((100)*1024*1024/8/(100*1024))
+ 7812
+ 1000000/((100)*1024/8/(100))
+ 7812
+ 1000000/((100)*128/(100))
+ 7812
+ 100000000/((100)*128)
+ 7812
+ 100000000/128
+ 781250
+ */
+#define RATE_TO_BTU 781250
+#define BURST_TIME_US burst_time_us
+
+static int xcio_ratewrite(XcIOContext *ioctxt, void *buf, int n){
+ static int budget = 0;
+ static struct timeval last_put = { 0 };
+ struct timeval now;
+ struct timespec delay;
+ long long delta;
+ int rc;
+
+ if (START_MBIT_RATE == 0)
+ return xcio_write(ioctxt, buf, n);
+
+ budget -= n;
+ if (budget < 0) {
+ if (MBIT_RATE != ombit_rate) {
+ BURST_TIME_US = RATE_TO_BTU / MBIT_RATE;
+ ombit_rate = MBIT_RATE;
+ xcio_info(ioctxt,
+ "rate limit: %d mbit/s burst budget %d slot time %d\n",
+ MBIT_RATE, BURST_BUDGET, BURST_TIME_US);
+ }
+ if (last_put.tv_sec == 0) {
+ budget += BURST_BUDGET;
+ gettimeofday(&last_put, NULL);
+ } else {
+ while (budget < 0) {
+ gettimeofday(&now, NULL);
+ delta = tv_delta(&now, &last_put);
+ while (delta > BURST_TIME_US) {
+ budget += BURST_BUDGET;
+ last_put.tv_usec += BURST_TIME_US;
+ if (last_put.tv_usec > 1000000) {
+ last_put.tv_usec -= 1000000;
+ last_put.tv_sec++;
+ }
+ delta -= BURST_TIME_US;
+ }
+ if (budget > 0)
+ break;
+ delay.tv_sec = 0;
+ delay.tv_nsec = 1000 * (BURST_TIME_US - delta);
+ while (delay.tv_nsec > 0)
+ if (nanosleep(&delay, &delay) == 0)
+ break;
+ }
+ }
+ }
+ rc = IOStream_write(ioctxt->io, buf, n);
+ return (rc == n ? 0 : rc);
+}
+
static int print_stats( int xc_handle, u32 domid,
int pages_sent, xc_shadow_control_stats_t *stats,
int print )
@@ -153,8 +232,8 @@ static int print_stats( int xc_handle, u32 domid,
gettimeofday(&wall_now, NULL);
- d0_cpu_now = xc_domain_get_cpu_usage( xc_handle, 0 )/1000;
- d1_cpu_now = xc_domain_get_cpu_usage( xc_handle, domid )/1000;
+ d0_cpu_now = xc_domain_get_cpu_usage( xc_handle, 0, /* FIXME */ 0 )/1000;
+ d1_cpu_now = xc_domain_get_cpu_usage( xc_handle, domid, /* FIXME */ 0 )/1000;
if ( (d0_cpu_now == -1) || (d1_cpu_now == -1) )
printf("ARRHHH!!\n");
@@ -168,12 +247,20 @@ static int print_stats( int xc_handle, u32 domid,
if ( print )
printf("delta %lldms, dom0 %d%%, target %d%%, sent %dMb/s, "
- "dirtied %dMb/s\n",
+ "dirtied %dMb/s %" PRId32 " pages\n",
wall_delta,
(int)((d0_cpu_delta*100)/wall_delta),
(int)((d1_cpu_delta*100)/wall_delta),
- (int)((pages_sent*PAGE_SIZE*8)/(wall_delta*1000)),
- (int)((stats->dirty_count*PAGE_SIZE*8)/(wall_delta*1000)));
+ (int)((pages_sent*PAGE_SIZE)/(wall_delta*(1000/8))),
+ (int)((stats->dirty_count*PAGE_SIZE)/(wall_delta*(1000/8))),
+ stats->dirty_count);
+
+ if (((stats->dirty_count*PAGE_SIZE)/(wall_delta*(1000/8))) > mbit_rate) {
+ mbit_rate = (int)((stats->dirty_count*PAGE_SIZE)/(wall_delta*(1000/8)))
+ + 50;
+ if (mbit_rate > MAX_MBIT_RATE)
+ mbit_rate = MAX_MBIT_RATE;
+ }
d0_cpu_last = d0_cpu_now;
d1_cpu_last = d1_cpu_now;
@@ -198,31 +285,32 @@ static int write_vmconfig(XcIOContext *ioctxt){
}
static int analysis_phase( int xc_handle, u32 domid,
- int nr_pfns, unsigned long *arr )
+ int nr_pfns, unsigned long *arr, int runs )
{
long long start, now;
xc_shadow_control_stats_t stats;
+ int j;
start = llgettimeofday();
- while ( 0 )
+ for (j = 0; j < runs; j++)
{
int i;
xc_shadow_control( xc_handle, domid,
- DOM0_SHADOW_CONTROL_OP_CLEAN2,
+ DOM0_SHADOW_CONTROL_OP_CLEAN,
arr, nr_pfns, NULL);
printf("#Flush\n");
- for ( i = 0; i < 100; i++ )
+ for ( i = 0; i < 40; i++ )
{
- usleep(10000);
+ usleep(50000);
now = llgettimeofday();
xc_shadow_control( xc_handle, domid,
DOM0_SHADOW_CONTROL_OP_PEEK,
NULL, 0, &stats);
- printf("now= %lld faults= %ld dirty= %ld dirty_net= %ld "
- "dirty_block= %ld\n",
+ printf("now= %lld faults= %" PRId32 " dirty= %" PRId32
+ " dirty_net= %" PRId32 " dirty_block= %" PRId32"\n",
((now-start)+500)/1000,
stats.fault_count, stats.dirty_count,
stats.dirty_net_count, stats.dirty_block_count);
@@ -232,13 +320,65 @@ static int analysis_phase( int xc_handle, u32 domid,
return -1;
}
+
+int suspend_and_state(int xc_handle, XcIOContext *ioctxt,
+ xc_domaininfo_t *info,
+ full_execution_context_t *ctxt)
+{
+ int i=0;
+
+ xcio_suspend_domain(ioctxt);
+
+retry:
+
+ if ( xc_domain_getfullinfo(xc_handle, ioctxt->domain, /* FIXME */ 0, info, ctxt) )
+ {
+ xcio_error(ioctxt, "Could not get full domain info");
+ return -1;
+ }
+
+ if ( (info->flags &
+ (DOMFLAGS_SHUTDOWN | (SHUTDOWN_suspend<<DOMFLAGS_SHUTDOWNSHIFT))) ==
+ (DOMFLAGS_SHUTDOWN | (SHUTDOWN_suspend<<DOMFLAGS_SHUTDOWNSHIFT)) )
+ {
+ return 0; // success
+ }
+
+ if ( info->flags & DOMFLAGS_PAUSED )
+ {
+ // try unpausing domain, wait, and retest
+ xc_domain_unpause( xc_handle, ioctxt->domain );
+
+ xcio_error(ioctxt, "Domain was paused. Wait and re-test. (%lx)",
+ info->flags);
+ usleep(10000); // 10ms
+
+ goto retry;
+ }
+
+
+ if( ++i < 100 )
+ {
+ xcio_error(ioctxt, "Retry suspend domain (%lx)",
+ info->flags);
+ usleep(10000); // 10ms
+ goto retry;
+ }
+
+ xcio_error(ioctxt, "Unable to suspend domain. (%lx)",
+ info->flags);
+
+ return -1;
+}
+
int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
{
- dom0_op_t op;
+ xc_domaininfo_t info;
+
int rc = 1, i, j, k, last_iter, iter = 0;
unsigned long mfn;
u32 domid = ioctxt->domain;
- int live = (ioctxt->flags & XCFLAGS_LIVE);
+ int live = (ioctxt->flags & XCFLAGS_LIVE);
int debug = (ioctxt->flags & XCFLAGS_DEBUG);
int sent_last_iter, skip_this_iter;
@@ -252,9 +392,6 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
/* A copy of the CPU context of the guest. */
full_execution_context_t ctxt;
- /* A copy of the domain's name. */
- char name[MAX_DOMAIN_NAME];
-
/* A table containg the type of each PFN (/not/ MFN!). */
unsigned long *pfn_type = NULL;
unsigned long *pfn_batch = NULL;
@@ -263,22 +400,23 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
unsigned long page[1024];
/* A copy of the pfn-to-mfn table frame list. */
- unsigned long *live_pfn_to_mfn_frame_list;
+ unsigned long *live_pfn_to_mfn_frame_list = NULL;
unsigned long pfn_to_mfn_frame_list[1024];
/* Live mapping of the table mapping each PFN to its current MFN. */
unsigned long *live_pfn_to_mfn_table = NULL;
/* Live mapping of system MFN to PFN table. */
unsigned long *live_mfn_to_pfn_table = NULL;
+ unsigned long mfn_to_pfn_table_start_mfn;
/* Live mapping of shared info structure */
- unsigned long *live_shinfo;
+ shared_info_t *live_shinfo = NULL;
/* base of the region in which domain memory is mapped */
unsigned char *region_base = NULL;
/* A temporary mapping, and a copy, of the guest's suspend record. */
- suspend_record_t *p_srec;
+ suspend_record_t *p_srec = NULL;
/* number of pages we're dealing with */
unsigned long nr_pfns;
@@ -296,85 +434,66 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
int needed_to_fix = 0;
int total_sent = 0;
+
+ MBIT_RATE = START_MBIT_RATE;
+
+ xcio_info(ioctxt, "xc_linux_save start %d\n", domid);
if (mlock(&ctxt, sizeof(ctxt))) {
xcio_perror(ioctxt, "Unable to mlock ctxt");
return 1;
}
- /* Ensure that the domain exists, and that it is stopped. */
- if ( xc_domain_pause(xc_handle, domid) ){
- xcio_perror(ioctxt, "Could not pause domain");
- goto out;
- }
-
- if ( xc_domain_getfullinfo( xc_handle, domid, &op, &ctxt) )
+ if ( xc_domain_getfullinfo( xc_handle, domid, /* FIXME */ 0, &info, &ctxt) )
{
xcio_error(ioctxt, "Could not get full domain info");
goto out;
}
- memcpy(name, op.u.getdomaininfo.name, sizeof(name));
- shared_info_frame = op.u.getdomaininfo.shared_info_frame;
+ shared_info_frame = info.shared_info_frame;
/* A cheesy test to see whether the domain contains valid state. */
if ( ctxt.pt_base == 0 ){
xcio_error(ioctxt, "Domain is not in a valid Linux guest OS state");
goto out;
}
+
+ nr_pfns = info.max_pages;
- /* Map the suspend-record MFN to pin it. The page must be owned by
- domid for this to succeed. */
- p_srec = mfn_mapper_map_single(xc_handle, domid,
- sizeof(*p_srec), PROT_READ,
- ctxt.cpu_ctxt.esi);
- if (!p_srec){
- xcio_error(ioctxt, "Couldn't map state record");
+ /* cheesy sanity check */
+ if ( nr_pfns > 1024*1024 ){
+ xcio_error(ioctxt, "Invalid state record -- pfn count out of range: %lu", nr_pfns);
goto out;
}
- nr_pfns = p_srec->nr_pfns;
- /* cheesy sanity check */
- if ( nr_pfns > 1024*1024 ){
- xcio_error(ioctxt, "Invalid state record -- pfn count out of range: %lu", nr_pfns);
+ /* Map the shared info frame */
+ live_shinfo = xc_map_foreign_range(xc_handle, domid,
+ PAGE_SIZE, PROT_READ,
+ shared_info_frame);
+
+ if (!live_shinfo){
+ xcio_error(ioctxt, "Couldn't map live_shinfo");
goto out;
}
/* the pfn_to_mfn_frame_list fits in a single page */
live_pfn_to_mfn_frame_list =
- mfn_mapper_map_single(xc_handle, domid,
+ xc_map_foreign_range(xc_handle, domid,
PAGE_SIZE, PROT_READ,
- p_srec->pfn_to_mfn_frame_list );
+ live_shinfo->arch.pfn_to_mfn_frame_list );
if (!live_pfn_to_mfn_frame_list){
xcio_error(ioctxt, "Couldn't map pfn_to_mfn_frame_list");
goto out;
}
- /* Track the mfn_to_pfn table down from the domains PT */
- {
- unsigned long *pgd;
- unsigned long mfn_to_pfn_table_start_mfn;
-
- pgd = mfn_mapper_map_single(xc_handle, domid,
- PAGE_SIZE, PROT_READ,
- ctxt.pt_base>>PAGE_SHIFT);
-
- mfn_to_pfn_table_start_mfn =
- pgd[HYPERVISOR_VIRT_START>>L2_PAGETABLE_SHIFT]>>PAGE_SHIFT;
-
- live_mfn_to_pfn_table =
- mfn_mapper_map_single(xc_handle, ~0UL,
- PAGE_SIZE*1024, PROT_READ,
- mfn_to_pfn_table_start_mfn );
- }
/* Map all the frames of the pfn->mfn table. For migrate to succeed,
the guest must not change which frames are used for this purpose.
(its not clear why it would want to change them, and we'll be OK
from a safety POV anyhow. */
- live_pfn_to_mfn_table = mfn_mapper_map_batch(xc_handle, domid,
+ live_pfn_to_mfn_table = xc_map_foreign_batch(xc_handle, domid,
PROT_READ,
live_pfn_to_mfn_frame_list,
(nr_pfns+1023)/1024 );
@@ -383,9 +502,17 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
goto out;
}
+ /* Setup the mfn_to_pfn table mapping */
+ mfn_to_pfn_table_start_mfn = xc_get_m2p_start_mfn( xc_handle );
+
+ live_mfn_to_pfn_table =
+ xc_map_foreign_range(xc_handle, DOMID_XEN,
+ PAGE_SIZE*1024, PROT_READ,
+ mfn_to_pfn_table_start_mfn );
/* Canonicalise the pfn-to-mfn table frame-number list. */
memcpy( pfn_to_mfn_frame_list, live_pfn_to_mfn_frame_list, PAGE_SIZE );
+
for ( i = 0; i < nr_pfns; i += 1024 ){
if ( !translate_mfn_to_pfn(&pfn_to_mfn_frame_list[i/1024]) ){
xcio_error(ioctxt, "Frame # in pfn-to-mfn frame list is not in pseudophys");
@@ -393,10 +520,11 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
}
}
- /* At this point, we can start the domain again if we're doing a
- live suspend */
- if( live ){
+ /* Domain is still running at this point */
+
+ if( live )
+ {
if ( xc_shadow_control( xc_handle, domid,
DOM0_SHADOW_CONTROL_OP_ENABLE_LOGDIRTY,
NULL, 0, NULL ) < 0 ) {
@@ -404,16 +532,22 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
goto out;
}
- if ( xc_domain_unpause(xc_handle, domid) < 0 ){
- xcio_error(ioctxt, "Couldn't unpause domain");
- goto out;
- }
-
last_iter = 0;
- sent_last_iter = 1<<20; /* 4GB of pages */
} else{
+ /* This is a non-live suspend. Issue the call back to get the
+ domain suspended */
+
last_iter = 1;
+
+ if ( suspend_and_state( xc_handle, ioctxt, &info, &ctxt) )
+ {
+ xcio_error(ioctxt, "Domain appears not to have suspended: %lx",
+ info.flags);
+ goto out;
+ }
+
}
+ sent_last_iter = 1<<20; /* 4GB of pages */
/* calculate the power of 2 order of nr_pfns, e.g.
15->4 16->4 17->5 */
@@ -421,7 +555,11 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
/* Setup to_send bitmap */
{
- int sz = (nr_pfns/8) + 8; /* includes slop at end of array */
+ /* size these for a maximal 4GB domain, to make interaction
+ with balloon driver easier. It's only user space memory,
+ ater all... (3x 128KB) */
+
+ int sz = ( 1<<20 ) / 8;
to_send = malloc( sz );
to_fix = calloc( 1, sz );
@@ -448,7 +586,7 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
}
- analysis_phase( xc_handle, domid, nr_pfns, to_skip );
+ analysis_phase( xc_handle, domid, nr_pfns, to_skip, 0 );
/* We want zeroed memory so use calloc rather than malloc. */
pfn_type = calloc(BATCH_SIZE, sizeof(unsigned long));
@@ -469,29 +607,27 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
* Quick belt and braces sanity check.
*/
#if DEBUG
- for ( i = 0; i < nr_pfns; i++ ){
- mfn = live_pfn_to_mfn_table[i];
-
- if( (live_mfn_to_pfn_table[mfn] != i) && (mfn != 0x80000004) )
- printf("i=0x%x mfn=%x live_mfn_to_pfn_table=%x\n",
- i,mfn,live_mfn_to_pfn_table[mfn]);
+ {
+ int err=0;
+ for ( i = 0; i < nr_pfns; i++ )
+ {
+ mfn = live_pfn_to_mfn_table[i];
+
+ if( (live_mfn_to_pfn_table[mfn] != i) && (mfn != 0xffffffffUL) )
+ {
+ printf("i=0x%x mfn=%lx live_mfn_to_pfn_table=%lx\n",
+ i,mfn,live_mfn_to_pfn_table[mfn]);
+ err++;
+ }
+ }
+ printf("Had %d unexplained entries in p2m table\n",err);
}
#endif
- /* Map the shared info frame */
- live_shinfo = mfn_mapper_map_single(xc_handle, domid,
- PAGE_SIZE, PROT_READ,
- shared_info_frame);
-
- if (!live_shinfo){
- xcio_error(ioctxt, "Couldn't map live_shinfo");
- goto out;
- }
/* Start writing out the saved-domain record. */
if ( xcio_write(ioctxt, "LinuxGuestRecord", 16) ||
- xcio_write(ioctxt, name, sizeof(name)) ||
xcio_write(ioctxt, &nr_pfns, sizeof(unsigned long)) ||
xcio_write(ioctxt, pfn_to_mfn_frame_list, PAGE_SIZE) ){
xcio_error(ioctxt, "Error writing header");
@@ -529,9 +665,10 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
but this is fast enough for the moment. */
if ( !last_iter &&
- xc_shadow_control(xc_handle, domid,
+ xc_shadow_control(xc_handle, domid,
DOM0_SHADOW_CONTROL_OP_PEEK,
- to_skip, nr_pfns, NULL) != nr_pfns ) {
+ to_skip, nr_pfns, NULL) != nr_pfns )
+ {
xcio_error(ioctxt, "Error peeking shadow bitmap");
goto out;
}
@@ -574,14 +711,15 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
pfn_batch[batch] = n;
pfn_type[batch] = live_pfn_to_mfn_table[n];
- if( pfn_type[batch] == 0x80000004 ){
+ if( ! is_mapped(pfn_type[batch]) )
+ {
/* not currently in pusedo-physical map -- set bit
in to_fix that we must send this page in last_iter
unless its sent sooner anyhow */
set_bit( n, to_fix );
if( iter>1 )
- DDPRINTF("netbuf race: iter %d, pfn %lx. mfn %lx\n",
+ DDPRINTF("netbuf race: iter %d, pfn %x. mfn %lx\n",
iter,n,pfn_type[batch]);
continue;
}
@@ -591,7 +729,7 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
!test_bit(n, to_send) )
{
needed_to_fix++;
- DPRINTF("Fix! iter %d, pfn %lx. mfn %lx\n",
+ DPRINTF("Fix! iter %d, pfn %x. mfn %lx\n",
iter,n,pfn_type[batch]);
}
@@ -600,12 +738,12 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
batch++;
}
- DDPRINTF("batch %d:%d (n=%d)\n", iter, batch, n);
+// DDPRINTF("batch %d:%d (n=%d)\n", iter, batch, n);
if ( batch == 0 )
goto skip; /* vanishingly unlikely... */
- if ( (region_base = mfn_mapper_map_batch(xc_handle, domid,
+ if ( (region_base = xc_map_foreign_batch(xc_handle, domid,
PROT_READ,
pfn_type,
batch)) == 0 ){
@@ -655,12 +793,12 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
continue;
}
- if ( ((pfn_type[j] & LTAB_MASK) == L1TAB) ||
- ((pfn_type[j] & LTAB_MASK) == L2TAB) ){
+ if ( ((pfn_type[j] & LTABTYPE_MASK) == L1TAB) ||
+ ((pfn_type[j] & LTABTYPE_MASK) == L2TAB) ){
memcpy(page, region_base + (PAGE_SIZE*j), PAGE_SIZE);
for ( k = 0;
- k < (((pfn_type[j] & LTAB_MASK) == L2TAB) ?
+ k < (((pfn_type[j] & LTABTYPE_MASK) == L2TAB) ?
(HYPERVISOR_VIRT_START >> L2_PAGETABLE_SHIFT) :
1024);
k++ ){
@@ -699,14 +837,14 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
} /* end of page table rewrite for loop */
- if ( xcio_write(ioctxt, page, PAGE_SIZE) ){
+ if ( xcio_ratewrite(ioctxt, page, PAGE_SIZE) ){
xcio_error(ioctxt, "Error when writing to state file (4)");
goto out;
}
} /* end of it's a PT page */ else { /* normal page */
- if ( xcio_write(ioctxt, region_base + (PAGE_SIZE*j),
+ if ( xcio_ratewrite(ioctxt, region_base + (PAGE_SIZE*j),
PAGE_SIZE) ){
xcio_error(ioctxt, "Error when writing to state file (5)");
goto out;
@@ -756,7 +894,8 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
if ( live )
{
if (
- /* ( sent_this_iter > (sent_last_iter * 0.95) ) || */
+ ( ( sent_this_iter > sent_last_iter ) &&
+ (mbit_rate == MAX_MBIT_RATE ) ) ||
(iter >= max_iters) ||
(sent_this_iter+skip_this_iter < 50) ||
(total_sent > nr_pfns*max_factor) )
@@ -764,11 +903,22 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
DPRINTF("Start last iteration\n");
last_iter = 1;
- xc_domain_pause( xc_handle, domid );
+ if ( suspend_and_state( xc_handle, ioctxt, &info, &ctxt) )
+ {
+ xcio_error(ioctxt, "Domain appears not to have suspended: %lx",
+ info.flags);
+ goto out;
+ }
+
+ xcio_info(ioctxt,
+ "SUSPEND flags %08lx shinfo %08lx eip %08lx "
+ "esi %08lx\n",info.flags,
+ info.shared_info_frame,
+ ctxt.cpu_ctxt.eip, ctxt.cpu_ctxt.esi );
}
if ( xc_shadow_control( xc_handle, domid,
- DOM0_SHADOW_CONTROL_OP_CLEAN2,
+ DOM0_SHADOW_CONTROL_OP_CLEAN,
to_send, nr_pfns, &stats ) != nr_pfns )
{
xcio_error(ioctxt, "Error flushing shadow PT");
@@ -796,16 +946,62 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
goto out;
}
- /* Get the final execution context */
- if ( xc_domain_getfullinfo( xc_handle, domid, &op, &ctxt) )
+ /* Send through a list of all the PFNs that were not in map at the close */
{
- xcio_perror(ioctxt, "Could not get full domain info");
+ unsigned int i,j;
+ unsigned int pfntab[1024];
+
+ for ( i = 0, j = 0; i < nr_pfns; i++ )
+ {
+ if ( ! is_mapped(live_pfn_to_mfn_table[i]) )
+ j++;
+ }
+
+ if ( xcio_write(ioctxt, &j, sizeof(unsigned int)) )
+ {
+ xcio_error(ioctxt, "Error when writing to state file (6a)");
+ goto out;
+ }
+
+ for ( i = 0, j = 0; i < nr_pfns; )
+ {
+ if ( ! is_mapped(live_pfn_to_mfn_table[i]) )
+ {
+ pfntab[j++] = i;
+ }
+ i++;
+ if ( j == 1024 || i == nr_pfns )
+ {
+ if ( xcio_write(ioctxt, &pfntab, sizeof(unsigned long)*j) )
+ {
+ xcio_error(ioctxt, "Error when writing to state file (6b)");
+ goto out;
+ }
+ j = 0;
+ }
+ }
+ }
+
+ /* Map the suspend-record MFN to pin it. The page must be owned by
+ domid for this to succeed. */
+ p_srec = xc_map_foreign_range(xc_handle, domid,
+ sizeof(*p_srec), PROT_READ,
+ ctxt.cpu_ctxt.esi);
+ if (!p_srec){
+ xcio_error(ioctxt, "Couldn't map suspend record");
+ goto out;
+ }
+
+ if (nr_pfns != p_srec->nr_pfns )
+ {
+ xcio_error(ioctxt, "Suspend record nr_pfns unexpected (%ld != %ld)",
+ p_srec->nr_pfns, nr_pfns);
goto out;
}
/* Canonicalise the suspend-record frame number. */
if ( !translate_mfn_to_pfn(&ctxt.cpu_ctxt.esi) ){
- xcio_error(ioctxt, "State record is not in range of pseudophys map");
+ xcio_error(ioctxt, "Suspend record is not in range of pseudophys map");
goto out;
}
@@ -830,9 +1026,15 @@ int xc_linux_save(int xc_handle, XcIOContext *ioctxt)
xcio_error(ioctxt, "Error when writing to state file (1)");
goto out;
}
- munmap(live_shinfo, PAGE_SIZE);
out:
+
+ if ( live_shinfo ) munmap(live_shinfo, PAGE_SIZE);
+ if ( p_srec ) munmap(p_srec, sizeof(*p_srec));
+ if ( live_pfn_to_mfn_frame_list ) munmap(live_pfn_to_mfn_frame_list, PAGE_SIZE);
+ if ( live_pfn_to_mfn_table ) munmap(live_pfn_to_mfn_table, nr_pfns*4 );
+ if ( live_mfn_to_pfn_table ) munmap(live_mfn_to_pfn_table, PAGE_SIZE*1024 );
+
if ( pfn_type != NULL ) free(pfn_type);
DPRINTF("Save exit rc=%d\n",rc);
return !!rc;
diff --git a/tools/libxc/xc_misc.c b/tools/libxc/xc_misc.c
index 0019ffe96b..0efd1f1972 100644
--- a/tools/libxc/xc_misc.c
+++ b/tools/libxc/xc_misc.c
@@ -52,18 +52,14 @@ int xc_physinfo(int xc_handle,
{
int ret;
dom0_op_t op;
- dom0_physinfo_t *got_info = &op.u.physinfo;
op.cmd = DOM0_PHYSINFO;
op.interface_version = DOM0_INTERFACE_VERSION;
- if((ret = do_dom0_op(xc_handle, &op))) return ret;
+ if ( (ret = do_dom0_op(xc_handle, &op)) != 0 )
+ return ret;
- put_info->ht_per_core = got_info->ht_per_core;
- put_info->cores = got_info->cores;
- put_info->total_pages = got_info->total_pages;
- put_info->free_pages = got_info->free_pages;
- put_info->cpu_khz = got_info->cpu_khz;
+ memcpy(put_info, &op.u.physinfo, sizeof(*put_info));
return 0;
}
@@ -78,10 +74,26 @@ int xc_sched_id(int xc_handle,
op.cmd = DOM0_SCHED_ID;
op.interface_version = DOM0_INTERFACE_VERSION;
- if((ret = do_dom0_op(xc_handle, &op))) return ret;
+ if ( (ret = do_dom0_op(xc_handle, &op)) != 0 )
+ return ret;
*sched_id = op.u.sched_id.sched_id;
return 0;
}
+int xc_perfc_control(int xc_handle,
+ u32 op,
+ xc_perfc_desc_t *desc)
+{
+ int rc;
+ dom0_op_t dop;
+
+ dop.cmd = DOM0_PERFCCONTROL;
+ dop.u.perfccontrol.op = op;
+ dop.u.perfccontrol.desc = desc;
+
+ rc = do_dom0_op(xc_handle, &dop);
+
+ return (rc == 0) ? dop.u.perfccontrol.nr_counters : rc;
+}
diff --git a/tools/libxc/xc_netbsd_build.c b/tools/libxc/xc_netbsd_build.c
deleted file mode 100644
index 833a533ab6..0000000000
--- a/tools/libxc/xc_netbsd_build.c
+++ /dev/null
@@ -1,706 +0,0 @@
-/******************************************************************************
- * xc_netbsd_build.c
- */
-
-#include "xc_private.h"
-#define ELFSIZE 32 /* XXX */
-#include "xc_elf.h"
-#include <zlib.h>
-
-#ifdef DEBUG
-#define DPRINTF(x) printf x
-#else
-#define DPRINTF(x)
-#endif
-
-static int loadelfimage(gzFile, void *, unsigned long *, unsigned long,
- unsigned long *, unsigned long *,
- unsigned long *, unsigned long *);
-
-#define ELFROUND (ELFSIZE / 8)
-
-#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED)
-#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER)
-
-static long get_tot_pages(int xc_handle, u32 domid)
-{
- dom0_op_t op;
- op.cmd = DOM0_GETDOMAININFO;
- op.u.getdomaininfo.domain = (domid_t)domid;
- op.u.getdomaininfo.ctxt = NULL;
- return (do_dom0_op(xc_handle, &op) < 0) ?
- -1 : op.u.getdomaininfo.tot_pages;
-}
-
-static int get_pfn_list(int xc_handle,
- u32 domid,
- unsigned long *pfn_buf,
- unsigned long max_pfns)
-{
- dom0_op_t op;
- int ret;
- op.cmd = DOM0_GETMEMLIST;
- op.u.getmemlist.domain = (domid_t)domid;
- op.u.getmemlist.max_pfns = max_pfns;
- op.u.getmemlist.buffer = pfn_buf;
-
- if ( mlock(pfn_buf, max_pfns * sizeof(unsigned long)) != 0 )
- return -1;
-
- ret = do_dom0_op(xc_handle, &op);
-
- (void)munlock(pfn_buf, max_pfns * sizeof(unsigned long));
-
- return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
-}
-
-static int setup_guestos(int xc_handle,
- u32 dom,
- gzFile kernel_gfd,
- unsigned long tot_pages,
- unsigned long *virt_startinfo_addr,
- unsigned long *virt_load_addr,
- full_execution_context_t *ctxt,
- const char *cmdline,
- unsigned long shared_info_frame,
- unsigned int control_evtchn)
-{
- l1_pgentry_t *vl1tab=NULL, *vl1e=NULL;
- l2_pgentry_t *vl2tab=NULL, *vl2e=NULL;
- unsigned long *page_array = NULL;
- int alloc_index, num_pt_pages;
- unsigned long l2tab;
- unsigned long l1tab;
- unsigned long count, pt_start;
- unsigned long symtab_addr = 0, symtab_len = 0;
- extended_start_info_t *start_info;
- shared_info_t *shared_info;
- unsigned long ksize;
- mmu_t *mmu = NULL;
- void *pm_handle = NULL;
- int i;
-
- if ( (pm_handle = init_pfn_mapper((domid_t)dom)) == NULL )
- goto error_out;
-
- if ( (page_array = malloc(tot_pages * sizeof(unsigned long))) == NULL )
- {
- PERROR("Could not allocate memory");
- goto error_out;
- }
-
- if ( get_pfn_list(xc_handle, dom, page_array, tot_pages) != tot_pages )
- {
- PERROR("Could not get the page frame list");
- goto error_out;
- }
-
- if (loadelfimage(kernel_gfd, pm_handle, page_array, tot_pages,
- virt_load_addr, &ksize, &symtab_addr, &symtab_len))
- goto error_out;
-
- /* ksize is kernel-image size rounded up to a page boundary. */
-
- alloc_index = tot_pages - 1;
-
- /* Count bottom-level PTs, rounding up. */
- num_pt_pages = (l1_table_offset(*virt_load_addr) + tot_pages + 1023)
- / 1024;
-
- /* We must also count the page directory. */
- num_pt_pages++;
-
- /* Index of first PT page. */
- pt_start = tot_pages - num_pt_pages;
-
- /*
- * First allocate page for page dir. Allocation goes backwards from the end
- * of the allocated physical address space.
- */
- l2tab = page_array[alloc_index] << PAGE_SHIFT;
- alloc_index--;
- ctxt->pt_base = l2tab;
-
- if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL )
- goto error_out;
-
- /* Initialise the page tables. */
- if ( (vl2tab = map_pfn_writeable(pm_handle, l2tab >> PAGE_SHIFT)) == NULL )
- goto error_out;
- memset(vl2tab, 0, PAGE_SIZE);
- vl2e = &vl2tab[l2_table_offset(*virt_load_addr)];
- for ( count = 0; count < tot_pages; count++ )
- {
- if ( ((unsigned long)vl1e & (PAGE_SIZE-1)) == 0 )
- {
- l1tab = page_array[alloc_index--] << PAGE_SHIFT;
- if ( vl1tab != NULL )
- unmap_pfn(pm_handle, vl1tab);
- if ( (vl1tab = map_pfn_writeable(pm_handle,
- l1tab >> PAGE_SHIFT)) == NULL )
- goto error_out;
- memset(vl1tab, 0, PAGE_SIZE);
- vl1e = &vl1tab[l1_table_offset(*virt_load_addr +
- (count<<PAGE_SHIFT))];
- *vl2e++ = l1tab | L2_PROT;
- }
-
- *vl1e = (page_array[count] << PAGE_SHIFT) | L1_PROT;
- if ( count >= pt_start )
- *vl1e &= ~_PAGE_RW;
- vl1e++;
-
- if ( add_mmu_update(xc_handle, mmu,
- (page_array[count] << PAGE_SHIFT) |
- MMU_MACHPHYS_UPDATE, count) )
- goto error_out;
- }
- unmap_pfn(pm_handle, vl1tab);
- unmap_pfn(pm_handle, vl2tab);
-
- /*
- * Pin down l2tab addr as page dir page - causes hypervisor to provide
- * correct protection for the page
- */
- if ( add_mmu_update(xc_handle, mmu,
- l2tab | MMU_EXTENDED_COMMAND, MMUEXT_PIN_L2_TABLE) )
- goto error_out;
-
- *virt_startinfo_addr =
- *virt_load_addr + ((alloc_index-1) << PAGE_SHIFT);
-
- start_info = map_pfn_writeable(pm_handle, page_array[alloc_index-1]);
- memset(start_info, 0, sizeof(*start_info));
- start_info->pt_base = *virt_load_addr + ((tot_pages-1) << PAGE_SHIFT);
- start_info->mod_start = symtab_addr;
- start_info->mod_len = symtab_len;
- start_info->nr_pages = tot_pages;
- start_info->shared_info = shared_info_frame << PAGE_SHIFT;
- start_info->flags = 0;
- start_info->domain_controller_evtchn = control_evtchn;
- strncpy(start_info->cmd_line, cmdline, MAX_CMDLINE);
- start_info->cmd_line[MAX_CMDLINE-1] = '\0';
- unmap_pfn(pm_handle, start_info);
-
- /* shared_info page starts its life empty. */
- shared_info = map_pfn_writeable(pm_handle, shared_info_frame);
- memset(shared_info, 0, PAGE_SIZE);
- /* Mask all upcalls... */
- for ( i = 0; i < MAX_VIRT_CPUS; i++ )
- shared_info->vcpu_data[i].evtchn_upcall_mask = 1;
- unmap_pfn(pm_handle, shared_info);
-
- /* Send the page update requests down to the hypervisor. */
- if ( finish_mmu_updates(xc_handle, mmu) )
- goto error_out;
-
- free(mmu);
- (void)close_pfn_mapper(pm_handle);
- free(page_array);
- return 0;
-
- error_out:
- if ( mmu != NULL )
- free(mmu);
- if ( pm_handle != NULL )
- (void)close_pfn_mapper(pm_handle);
- if ( page_array == NULL )
- free(page_array);
- return -1;
-}
-
-int xc_netbsd_build(int xc_handle,
- u32 domid,
- const char *image_name,
- const char *cmdline,
- unsigned int control_evtchn)
-{
- dom0_op_t launch_op, op;
- unsigned long load_addr;
- long tot_pages;
- int kernel_fd = -1;
- gzFile kernel_gfd = NULL;
- int rc, i;
- full_execution_context_t st_ctxt, *ctxt = &st_ctxt;
- unsigned long virt_startinfo_addr;
-
- if ( (tot_pages = get_tot_pages(xc_handle, domid)) < 0 )
- {
- PERROR("Could not find total pages for domain");
- return 1;
- }
-
- kernel_fd = open(image_name, O_RDONLY);
- if ( kernel_fd < 0 )
- {
- PERROR("Could not open kernel image");
- return 1;
- }
-
- if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL )
- {
- PERROR("Could not allocate decompression state for state file");
- close(kernel_fd);
- return 1;
- }
-
- if ( mlock(&st_ctxt, sizeof(st_ctxt) ) )
- {
- PERROR("Unable to mlock ctxt");
- return 1;
- }
-
- op.cmd = DOM0_GETDOMAININFO;
- op.u.getdomaininfo.domain = (domid_t)domid;
- op.u.getdomaininfo.ctxt = ctxt;
- if ( (do_dom0_op(xc_handle, &op) < 0) ||
- ((u32)op.u.getdomaininfo.domain != domid) )
- {
- PERROR("Could not get info on domain");
- goto error_out;
- }
- if ( !(op.u.getdomaininfo.flags & DOMFLAGS_PAUSED) ||
- (op.u.getdomaininfo.ctxt->pt_base != 0) )
- {
- ERROR("Domain is already constructed");
- goto error_out;
- }
-
- if ( setup_guestos(xc_handle, domid, kernel_gfd, tot_pages,
- &virt_startinfo_addr,
- &load_addr, &st_ctxt, cmdline,
- op.u.getdomaininfo.shared_info_frame,
- control_evtchn) < 0 )
- {
- ERROR("Error constructing guest OS");
- goto error_out;
- }
-
- if ( kernel_fd >= 0 )
- close(kernel_fd);
- if( kernel_gfd )
- gzclose(kernel_gfd);
-
- ctxt->flags = 0;
-
- /*
- * Initial register values:
- * DS,ES,FS,GS = FLAT_GUESTOS_DS
- * CS:EIP = FLAT_GUESTOS_CS:start_pc
- * SS:ESP = FLAT_GUESTOS_DS:start_stack
- * ESI = start_info
- * [EAX,EBX,ECX,EDX,EDI,EBP are zero]
- * EFLAGS = IF | 2 (bit 1 is reserved and should always be 1)
- */
- ctxt->cpu_ctxt.ds = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.es = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.fs = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.gs = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.ss = FLAT_GUESTOS_DS;
- ctxt->cpu_ctxt.cs = FLAT_GUESTOS_CS;
- ctxt->cpu_ctxt.eip = load_addr;
- ctxt->cpu_ctxt.esp = virt_startinfo_addr;
- ctxt->cpu_ctxt.esi = virt_startinfo_addr;
- ctxt->cpu_ctxt.eflags = (1<<9) | (1<<2);
-
- /* FPU is set up to default initial state. */
- memset(ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt));
-
- /* Virtual IDT is empty at start-of-day. */
- for ( i = 0; i < 256; i++ )
- {
- ctxt->trap_ctxt[i].vector = i;
- ctxt->trap_ctxt[i].cs = FLAT_GUESTOS_CS;
- }
- ctxt->fast_trap_idx = 0;
-
- /* No LDT. */
- ctxt->ldt_ents = 0;
-
- /* Use the default Xen-provided GDT. */
- ctxt->gdt_ents = 0;
-
- /* Ring 1 stack is the initial stack. */
- ctxt->guestos_ss = FLAT_GUESTOS_DS;
- ctxt->guestos_esp = virt_startinfo_addr;
-
- /* No debugging. */
- memset(ctxt->debugreg, 0, sizeof(ctxt->debugreg));
-
- /* No callback handlers. */
- ctxt->event_callback_cs = FLAT_GUESTOS_CS;
- ctxt->event_callback_eip = 0;
- ctxt->failsafe_callback_cs = FLAT_GUESTOS_CS;
- ctxt->failsafe_callback_eip = 0;
-
- memset( &launch_op, 0, sizeof(launch_op) );
-
- launch_op.u.builddomain.domain = (domid_t)domid;
- launch_op.u.builddomain.ctxt = ctxt;
- launch_op.cmd = DOM0_BUILDDOMAIN;
- rc = do_dom0_op(xc_handle, &launch_op);
-
- return rc;
-
- error_out:
- if ( kernel_fd >= 0 )
- close(kernel_fd);
- if( kernel_gfd )
- gzclose(kernel_gfd);
-
- return -1;
-}
-
-#define MYSEEK_BUFSIZE 1024
-static off_t
-myseek(gzFile gfd, off_t offset, int whence)
-{
- unsigned char tmp[MYSEEK_BUFSIZE];
- int c;
-
- if ( offset < 0 )
- {
- ERROR("seek back not supported");
- return -1;
- }
-
- while ( offset != 0 )
- {
- c = offset;
- if ( c > MYSEEK_BUFSIZE )
- c = MYSEEK_BUFSIZE;
- if ( gzread(gfd, tmp, c) != c )
- {
- PERROR("Error seeking in image.");
- return -1;
- }
- offset -= c;
- }
-
- return 0; /* XXX */
-}
-
-/*
- * NetBSD memory layout:
- *
- * ---------------- *virt_load_addr = ehdr.e_entry (0xc0100000)
- * | kernel text |
- * | |
- * ----------------
- * | kernel data |
- * | |
- * ----------------
- * | kernel bss |
- * | |
- * ---------------- *symtab_addr
- * | symtab size | = *symtab_len
- * ----------------
- * | elf header | offsets to symbol sections mangled to be relative
- * | | to headers location
- * ----------------
- * | sym section |
- * | headers |
- * ----------------
- * | sym sections |
- * | |
- * ---------------- *symtab_addr + *symtab_len
- * | padding |
- * ---------------- ehdr.e_entry + *ksize << PAGE_SHIFT
- */
-
-#define IS_TEXT(p) (p.p_flags & PF_X)
-#define IS_DATA(p) (p.p_flags & PF_W)
-#define IS_BSS(p) (p.p_filesz < p.p_memsz)
-
-static int
-loadelfimage(gzFile kernel_gfd, void *pm_handle, unsigned long *page_array,
- unsigned long tot_pages, unsigned long *virt_load_addr,
- unsigned long *ksize, unsigned long *symtab_addr,
- unsigned long *symtab_len)
-{
- Elf_Ehdr ehdr;
- Elf_Phdr *phdr;
- Elf_Shdr *shdr;
- void *vaddr;
- char page[PAGE_SIZE], *p;
- unsigned long iva, maxva, symva;
- int c, curpos, h, i, ret, s;
-
- ret = -1;
- phdr = NULL;
- p = NULL;
- maxva = 0;
-
- if ( gzread(kernel_gfd, &ehdr, sizeof(Elf_Ehdr)) != sizeof(Elf_Ehdr) )
- {
- PERROR("Error reading kernel image ELF header.");
- goto out;
- }
- curpos = sizeof(Elf_Ehdr);
-
- if ( !IS_ELF(ehdr) )
- {
- PERROR("Image does not have an ELF header.");
- goto out;
- }
-
- *virt_load_addr = ehdr.e_entry;
-
- if ( (*virt_load_addr & (PAGE_SIZE-1)) != 0 )
- {
- ERROR("We can only deal with page-aligned load addresses");
- goto out;
- }
-
- if ( (*virt_load_addr + (tot_pages << PAGE_SHIFT)) >
- HYPERVISOR_VIRT_START )
- {
- ERROR("Cannot map all domain memory without hitting Xen space");
- goto out;
- }
-
-
- phdr = malloc(ehdr.e_phnum * sizeof(Elf_Phdr));
- if ( phdr == NULL )
- {
- ERROR("Cannot allocate memory for Elf_Phdrs");
- goto out;
- }
-
- if ( myseek(kernel_gfd, ehdr.e_phoff - curpos, SEEK_SET) == -1 )
- {
- ERROR("Seek to program header failed");
- goto out;
- }
- curpos = ehdr.e_phoff;
-
- if ( gzread(kernel_gfd, phdr, ehdr.e_phnum * sizeof(Elf_Phdr)) !=
- ehdr.e_phnum * sizeof(Elf_Phdr) )
- {
- PERROR("Error reading kernel image ELF program header.");
- goto out;
- }
- curpos += ehdr.e_phnum * sizeof(Elf_Phdr);
-
- /* Copy run-time 'load' segments that are writeable and/or executable. */
- for ( h = 0; h < ehdr.e_phnum; h++ )
- {
- if ( (phdr[h].p_type != PT_LOAD) ||
- ((phdr[h].p_flags & (PF_W|PF_X)) == 0) )
- continue;
-
- if ( IS_TEXT(phdr[h]) || IS_DATA(phdr[h]) )
- {
- if ( myseek(kernel_gfd, phdr[h].p_offset - curpos,
- SEEK_SET) == -1 )
- {
- ERROR("Seek to section failed");
- goto out;
- }
- curpos = phdr[h].p_offset;
-
- for ( iva = phdr[h].p_vaddr;
- iva < phdr[h].p_vaddr + phdr[h].p_filesz;
- iva += c)
- {
- c = PAGE_SIZE - (iva & (PAGE_SIZE - 1));
- if (iva + c > phdr[h].p_vaddr + phdr[h].p_filesz)
- c = phdr[h].p_vaddr + phdr[h].p_filesz - iva;
- if ( gzread(kernel_gfd, page, c) != c )
- {
- PERROR("Error reading kernel image page.");
- goto out;
- }
- curpos += c;
- vaddr = map_pfn_writeable(pm_handle,
- page_array[(iva - *virt_load_addr)
- >> PAGE_SHIFT]);
- if ( vaddr == NULL )
- {
- ERROR("Couldn't map guest memory");
- goto out;
- }
- DPRINTF(("copy page %p to %p, count 0x%x\n", (void *)iva,
- vaddr + (iva & (PAGE_SIZE - 1)), c));
- memcpy(vaddr + (iva & (PAGE_SIZE - 1)), page, c);
- unmap_pfn(pm_handle, vaddr);
- }
-
- if ( phdr[h].p_vaddr + phdr[h].p_filesz > maxva )
- maxva = phdr[h].p_vaddr + phdr[h].p_filesz;
- }
-
- if ( IS_BSS(phdr[h]) )
- {
- /* XXX maybe clear phdr[h].p_memsz bytes from
- phdr[h].p_vaddr + phdr[h].p_filesz ??? */
- if (phdr[h].p_vaddr + phdr[h].p_memsz > maxva)
- maxva = phdr[h].p_vaddr + phdr[h].p_memsz;
- DPRINTF(("bss from %p to %p, maxva %p\n",
- (void *)(phdr[h].p_vaddr + phdr[h].p_filesz),
- (void *)(phdr[h].p_vaddr + phdr[h].p_memsz),
- (void *)maxva));
- }
- }
-
- p = malloc(sizeof(int) + sizeof(Elf_Ehdr) +
- ehdr.e_shnum * sizeof(Elf_Shdr));
- if ( p == NULL )
- {
- ERROR("Cannot allocate memory for Elf_Shdrs");
- goto out;
- }
-
- shdr = (Elf_Shdr *)(p + sizeof(int) + sizeof(Elf_Ehdr));
-
- if ( myseek(kernel_gfd, ehdr.e_shoff - curpos, SEEK_SET) == -1 )
- {
- ERROR("Seek to symbol header failed");
- goto out;
- }
- curpos = ehdr.e_shoff;
-
- if ( gzread(kernel_gfd, shdr, ehdr.e_shnum * sizeof(Elf_Shdr)) !=
- ehdr.e_shnum * sizeof(Elf_Shdr) )
- {
- PERROR("Error reading kernel image ELF symbol header.");
- goto out;
- }
- curpos += ehdr.e_shnum * sizeof(Elf_Shdr);
-
- maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
- symva = maxva;
- maxva += sizeof(int);
- *symtab_addr = maxva;
- *symtab_len = 0;
- maxva += sizeof(Elf_Ehdr) + ehdr.e_shnum * sizeof(Elf_Shdr);
- maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
-
- /* Copy kernel string / symbol tables into physical memory */
- for ( h = 0; h < ehdr.e_shnum; h++ )
- {
- if ( shdr[h].sh_type == SHT_STRTAB )
- {
- /* Look for a strtab @i linked to symtab @h. */
- for ( i = 0; i < ehdr.e_shnum; i++ )
- if ( (shdr[i].sh_type == SHT_SYMTAB) &&
- (shdr[i].sh_link == h) )
- break;
- /* Skip symtab @h if we found no corresponding strtab @i. */
- if ( i == ehdr.e_shnum )
- {
- shdr[h].sh_offset = 0;
- continue;
- }
- }
-
- if ( (shdr[h].sh_type == SHT_STRTAB) ||
- (shdr[h].sh_type == SHT_SYMTAB) )
- {
- if ( myseek(kernel_gfd, shdr[h].sh_offset - curpos,
- SEEK_SET) == -1 )
- {
- ERROR("Seek to symbol section failed");
- goto out;
- }
- curpos = shdr[h].sh_offset;
-
- /* Mangled to be based on ELF header location. */
- shdr[h].sh_offset = maxva - *symtab_addr;
-
- DPRINTF(("copy section %d, size 0x%x\n", h, shdr[h].sh_size));
- for ( i = 0; i < shdr[h].sh_size; i += c, maxva += c )
- {
- c = PAGE_SIZE - (maxva & (PAGE_SIZE - 1));
- if ( c > (shdr[h].sh_size - i) )
- c = shdr[h].sh_size - i;
- if ( gzread(kernel_gfd, page, c) != c )
- {
- PERROR("Error reading kernel image page.");
- goto out;
- }
- curpos += c;
-
- vaddr = map_pfn_writeable(pm_handle,
- page_array[(maxva - *virt_load_addr)
- >> PAGE_SHIFT]);
- if ( vaddr == NULL )
- {
- ERROR("Couldn't map guest memory");
- goto out;
- }
- DPRINTF(("copy page %p to %p, count 0x%x\n", (void *)maxva,
- vaddr + (maxva & (PAGE_SIZE - 1)), c));
- memcpy(vaddr + (maxva & (PAGE_SIZE - 1)), page, c);
- unmap_pfn(pm_handle, vaddr);
- }
-
- *symtab_len += shdr[h].sh_size;
- maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
-
- }
- shdr[h].sh_name = 0; /* Name is NULL. */
- }
-
- if ( *symtab_len == 0 )
- {
- DPRINTF(("no symbol table\n"));
- *symtab_addr = 0;
- ret = 0;
- goto out;
- }
-
- DPRINTF(("sym header va %p from %p/%p size %x/%x\n", (void *)symva,
- shdr, p, ehdr.e_shnum * sizeof(Elf_Shdr),
- ehdr.e_shnum * sizeof(Elf_Shdr) + sizeof(Elf_Ehdr)));
- ehdr.e_phoff = 0;
- ehdr.e_shoff = sizeof(Elf_Ehdr);
- ehdr.e_phentsize = 0;
- ehdr.e_phnum = 0;
- ehdr.e_shstrndx = SHN_UNDEF;
- memcpy(p + sizeof(int), &ehdr, sizeof(Elf_Ehdr));
- *(int *)p = maxva - *symtab_addr;
-
- /* Copy total length, crafted ELF header and section header table */
- s = sizeof(int) + sizeof(Elf_Ehdr) + ehdr.e_shnum * sizeof(Elf_Shdr);
- for ( i = 0; i < s; i += c, symva += c )
- {
- c = PAGE_SIZE - (symva & (PAGE_SIZE - 1));
- if ( c > s - i )
- c = s - i;
- vaddr = map_pfn_writeable(pm_handle,
- page_array[(symva - *virt_load_addr)
- >> PAGE_SHIFT]);
- if ( vaddr == NULL )
- {
- ERROR("Couldn't map guest memory");
- goto out;
- }
- DPRINTF(("copy page %p to %p, count 0x%x\n", (void *)symva,
- vaddr + (symva & (PAGE_SIZE - 1)), c));
- memcpy(vaddr + (symva & (PAGE_SIZE - 1)), p + i,
- c);
- unmap_pfn(pm_handle, vaddr);
- }
-
- *symtab_len = maxva - *symtab_addr;
-
- ret = 0;
-
- out:
- if ( ret == 0 )
- {
- maxva = (maxva + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
- *ksize = (maxva - *virt_load_addr) >> PAGE_SHIFT;
-
- DPRINTF(("virt_addr %p, kpages 0x%lx, symtab_addr %p, symtab_len %p\n",
- (void *)*virt_load_addr, *ksize, (void *)*symtab_addr,
- (void *)*symtab_len));
- }
-
- if ( phdr != NULL )
- free(phdr);
- if ( p != NULL )
- free(p);
- return ret;
-}
diff --git a/tools/libxc/xc_plan9_build.c b/tools/libxc/xc_plan9_build.c
new file mode 100755
index 0000000000..0394938c17
--- /dev/null
+++ b/tools/libxc/xc_plan9_build.c
@@ -0,0 +1,699 @@
+/******************************************************************************
+ * xc_plan9_build.c
+ * derived from xc_linux_build.c
+ */
+
+#include "xc_private.h"
+
+#include <zlib.h>
+
+#define DEBUG 1
+#ifdef DEBUG
+#define DPRINTF(x) printf x; fflush(stdout);
+#else
+#define DPRINTF(x)
+#endif
+
+#include "plan9a.out.h"
+
+/* really TOS which means stack starts at 0x2000, and uses page 1*/
+#define STACKPAGE 2
+struct Exec header, origheader;
+
+typedef struct page {
+ char data[PAGE_SIZE];
+} PAGE;
+
+
+int
+memcpy_toguest(int xc_handle, u32 dom, void *v, int size,
+ unsigned long *page_array, unsigned int to_page)
+{
+ int ret;
+ unsigned char *cp = v;
+ unsigned int whichpage;
+ unsigned char *vaddr;
+
+// DPRINTF(("memcpy_to_guest: to_page 0x%x, count %d\n", to_page, size));
+ for (ret = 0, whichpage = to_page; size > 0;
+ whichpage++, size -= PAGE_SIZE, cp += PAGE_SIZE) {
+
+ // DPRINTF (("map_pfn_writeable(%p, 0x%lx)\n", pm_handle,
+// page_array[whichpage]));
+ vaddr = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ page_array[whichpage]);
+ // DPRINTF (("vaddr is %p\n", vaddr));
+ if (vaddr == NULL) {
+ ret = -1;
+ ERROR("Couldn't map guest memory");
+ goto out;
+ }
+ // DPRINTF (("copy %p to %p, count 0x%x\n", cp, vaddr, 4096));
+ memcpy(vaddr, cp, 4096);
+ munmap(vaddr, PAGE_SIZE);
+ // DPRINTF (("Did %ud'th pages\n", whichpage));
+ }
+ out:
+ return ret;
+}
+
+/* this is a function which can go away. It dumps a hunk of
+ * guest pages to a file (/tmp/dumpit); handy for debugging
+ * your image builder.
+ * Xen guys, nuke this if you wish.
+ */
+void
+dumpit(int xc_handle, u32 dom,
+ int start_page, int tot, unsigned long *page_array)
+{
+ int i, ofd;
+ unsigned char *vaddr;
+
+ ofd = open("/tmp/dumpit", O_RDWR);
+ for (i = start_page; i < tot; i++) {
+ vaddr = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
+ PROT_READ | PROT_WRITE,
+ page_array[i]);
+ if (!vaddr) {
+ fprintf(stderr, "Page %d\n", i);
+ perror("shit");
+ read(0, &i, 1);
+ return;
+ }
+ write(ofd, vaddr, 4096);
+ munmap(vaddr, PAGE_SIZE);
+ }
+}
+int
+blah(char *b)
+{
+ fprintf(stderr, "Error in xc_plan9_build!\n");
+ perror(b);
+ return errno;
+}
+
+/* swap bytes. For plan 9 headers */
+void
+swabby(unsigned long *s, char *name)
+{
+ unsigned long it;
+ it = ((*s & 0xff000000) >> 24) | ((*s & 0xff0000) >> 8) |
+ ((*s & 0xff00) << 8) | ((*s & 0xff) << 24);
+ DPRINTF(("Item %s is 0x%lx\n", name, it));
+ *s = it;
+}
+
+void
+plan9header(Exec * header)
+{
+ /* header is big-endian */
+ swabby(&header->magic, "magic");
+ swabby(&header->text, "text");
+ swabby(&header->data, "data");
+ swabby(&header->bss, "bss");
+ swabby(&header->syms, "syms");
+ swabby(&header->entry, "entry");
+ swabby(&header->spsz, "spsz");
+ swabby(&header->pcsz, "pcsz");
+
+}
+
+static int
+ loadp9image(gzFile kernel_gfd, int xc_handle, u32 dom,
+ unsigned long *page_array,
+ unsigned long tot_pages, unsigned long *virt_load_addr,
+ unsigned long *ksize, unsigned long *symtab_addr,
+ unsigned long *symtab_len,
+ unsigned long *first_data_page, unsigned long *pdb_page);
+
+#define P9ROUND (P9SIZE / 8)
+
+#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED)
+#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER)
+
+static int
+setup_guest(int xc_handle,
+ u32 dom,
+ gzFile kernel_gfd,
+ unsigned long tot_pages,
+ unsigned long *virt_startinfo_addr,
+ unsigned long *virt_load_addr,
+ full_execution_context_t * ctxt,
+ const char *cmdline,
+ unsigned long shared_info_frame,
+ unsigned int control_evtchn,
+ int flags)
+{
+ l1_pgentry_t *vl1e = NULL;
+ l2_pgentry_t *vl2tab = NULL, *vl2e = NULL;
+ unsigned long *cpage_array = NULL;
+ unsigned long *pte_array = NULL;
+ unsigned long l2tab;
+ unsigned long l1tab;
+ unsigned long count;
+ unsigned long symtab_addr = 0, symtab_len = 0;
+ start_info_t *start_info;
+ shared_info_t *shared_info;
+ unsigned long ksize;
+ mmu_t *mmu = NULL;
+ int i;
+ unsigned long first_page_after_kernel,
+ first_data_page,
+ page_array_page;
+ unsigned long cpu0pdb, cpu0pte, cpu0ptelast;
+ unsigned long /*last_pfn, */ tot_pte_pages;
+
+ DPRINTF(("tot pages is %ld\n", tot_pages));
+ if ((cpage_array = malloc(tot_pages * sizeof (unsigned long))) == NULL) {
+ PERROR("Could not allocate cpage array");
+ goto error_out;
+ }
+
+ if (xc_get_pfn_list(xc_handle, dom, cpage_array, tot_pages) != tot_pages) {
+ PERROR("Could not get the page frame list");
+ goto error_out;
+ }
+
+ for (i = 0; i < 64; i++)
+ DPRINTF(("First %d page is 0x%lx\n", i, cpage_array[i]));
+
+ tot_pte_pages = tot_pages >> 10;
+ DPRINTF(("Page range is 0 to 0x%lx, which requires 0x%lx pte pages\n",
+ tot_pte_pages, tot_pte_pages));
+
+ if (loadp9image(kernel_gfd, xc_handle, dom, cpage_array, tot_pages,
+ virt_load_addr, &ksize, &symtab_addr, &symtab_len,
+ &first_data_page, &first_page_after_kernel))
+ goto error_out;
+ DPRINTF(("First data page is 0x%lx\n", first_data_page));
+ DPRINTF(("First page after kernel is 0x%lx\n",
+ first_page_after_kernel));
+
+ /*
+ NEED TO INCREMENT first page after kernel by:
+ + 1 (pdb)
+ + tot_pte_pages (pte)
+ + tot_pte_pages (page_array)
+ */
+ /* SO, have to copy the first kernel pages pfns right into the
+ * page_array, then do identity maps for the rest.
+ */
+ DPRINTF(("mapped kernel pages\n"));
+
+ /* now loop over all ptes and store into the page_array, so as
+ * to get the identity map.
+ */
+ if ((pte_array =
+ malloc(tot_pte_pages * 1024 * sizeof (unsigned long))) == NULL) {
+ PERROR("Could not allocate pte array");
+ goto error_out;
+ }
+
+ /* plan 9 on startup expects a "l2" (xen parlance) at 0x2000,
+ * this "l2" should have one PTE pointer for a va of 0x80000000.
+ * and an l1 (PTEs to you) at 0x3000. (physical).
+ * the PTEs should map the first 4M of memory.
+ */
+ /* get a physical address for the L2. This means take the PFN and
+ * shift left.
+ */
+ /* this terminology is plan 9 terminology.
+ * pdb is essentially the Xen L2. 'Page Directory Block'?
+ * I need to ask JMK.
+ * cpupte is the pte array.
+ * Plan 9 counts on these being set up for cpu0.
+ * SO: cpu0pdb (Xen L2)
+ * and cpupte (Xen L1)
+ */
+ /* cpu0pdb is right after kernel */
+ cpu0pdb = first_page_after_kernel;
+ /* cpu0pte comes right after cpu0pdb */
+ cpu0pte = cpu0pdb + 1;
+ /* number of the past cpu0pte page */
+ cpu0ptelast = cpu0pte + tot_pte_pages - 1;
+ /* first page of the page array (mfn) */
+ page_array_page = cpu0ptelast + 1;
+
+ DPRINTF(("cpu0pdb 0x%lx, cpu0pte 0x%lx cpu0ptelast 0x%lx\n", cpu0pdb,
+ cpu0pte, cpu0ptelast));
+ l2tab = cpage_array[cpu0pdb] << PAGE_SHIFT;
+ DPRINTF(("l2tab 0x%lx\n", l2tab));
+ ctxt->pt_base = l2tab;
+
+ /* get a physical address for the L1. This means take the PFN and
+ * shift left.
+ */
+ l1tab = cpage_array[cpu0pte] << PAGE_SHIFT;
+ DPRINTF(("l1tab 0x%lx\n", l1tab));
+ if ((mmu = init_mmu_updates(xc_handle, dom)) == NULL)
+ goto error_out;
+ DPRINTF(("now map in l2tab\n"));
+
+ /* Initialise the page tables. */
+ /* mmap in the l2tab */
+ if ((vl2tab = xc_map_foreign_range(xc_handle, dom,
+ PAGE_SIZE, PROT_READ | PROT_WRITE,
+ l2tab >> PAGE_SHIFT)) == NULL)
+ goto error_out;
+ DPRINTF(("vl2tab 0x%p\n", vl2tab));
+ /* now we have the cpu0pdb for the kernel, starting at 0x2000,
+ * so we can plug in the physical pointer to the 0x3000 pte
+ */
+ /* zero it */
+ memset(vl2tab, 0, PAGE_SIZE);
+ /* get a pointer in the l2tab for the virt_load_addr */
+ DPRINTF(("&vl2tab[l2_table_offset(*virt_load_addr)] is 0x%p[0x%lx]\n",
+ &vl2tab[l2_table_offset(*virt_load_addr)],
+ l2_table_offset(*virt_load_addr)));
+
+ vl2e = &vl2tab[l2_table_offset(*virt_load_addr)];
+
+ /* OK, for all the available PTE, set the PTE pointer up */
+ DPRINTF(("For i = %ld to %ld ...\n", cpu0pte, cpu0ptelast));
+ for (i = cpu0pte; i <= cpu0ptelast; i++) {
+ DPRINTF(("Index %d Set %p to 0x%lx\n", i, vl2e,
+ (cpage_array[i] << PAGE_SHIFT) | L2_PROT));
+ *vl2e++ = (cpage_array[i] << PAGE_SHIFT) | L2_PROT;
+ }
+
+ /* unmap it ... */
+ munmap(vl2tab, PAGE_SIZE);
+
+ /* for the pages from virt_load_pointer to the end of this
+ * set of PTEs, map in the PFN for that VA
+ */
+ for (vl1e = (l1_pgentry_t *) pte_array, count = 0;
+ count < tot_pte_pages * 1024; count++, vl1e++) {
+
+ *vl1e = cpage_array[count];
+ if (!cpage_array[count])
+ continue;
+ /* set in the PFN for this entry */
+ *vl1e = (cpage_array[count] << PAGE_SHIFT) | L1_PROT;
+/*
+ DPRINTF (("vl1e # %d 0x%lx gets 0x%lx\n",
+ count, vl1e, *vl1e));
+*/
+ if ((count >= cpu0pdb) && (count <= cpu0ptelast)) {
+ //DPRINTF((" Fix up page %d as it is in pte ville: ", count));
+ *vl1e &= ~_PAGE_RW;
+ DPRINTF(("0x%lx\n", *vl1e));
+ }
+ if ((count >= (0x100000 >> 12))
+ && (count < (first_data_page >> 12))) {
+ //DPRINTF((" Fix up page %d as it is in text ", count));
+ *vl1e &= ~_PAGE_RW;
+ //DPRINTF (("0x%lx\n", *vl1e));
+ }
+ }
+ /* special thing. Pre-map the shared info page */
+ vl1e = &pte_array[2];
+ *vl1e = (shared_info_frame << PAGE_SHIFT) | L1_PROT;
+ DPRINTF(("v1l1 %p, has value 0x%lx\n", vl1e, *(unsigned long *) vl1e));
+ /* another special thing. VA 80005000 has to point to 80006000 */
+ /* this is a Plan 9 thing -- the 'mach' pointer */
+ /* 80005000 is the mach pointer per-cpu, and the actual
+ * mach pointers are 80006000, 80007000 etc.
+ */
+ vl1e = &pte_array[5];
+ *vl1e = (cpage_array[6] << PAGE_SHIFT) | L1_PROT;
+
+ /* OK, it's all set up, copy it in */
+ memcpy_toguest(xc_handle, dom, pte_array,
+ (tot_pte_pages * 1024 * sizeof (unsigned long) /**/),
+ cpage_array, cpu0pte);
+
+ /* We really need to have the vl1tab unmapped or the add_mmu_update
+ * below will fail bigtime.
+ */
+ /* Xen guys: remember my errors on domain exit? Something I'm doing
+ * wrong in here? We never did find out ...
+ */
+ /* get rid of the entries we can not use ... */
+ memcpy_toguest(xc_handle, dom, cpage_array,
+ (tot_pte_pages * 1024 * sizeof (unsigned long) /**/),
+ cpage_array, page_array_page);
+ /* last chance to dump all of memory */
+ // dumpit(xc_handle, dom, 0 /*0x100000>>12*/, tot_pages, cpage_array) ;
+ /*
+ * Pin down l2tab addr as page dir page - causes hypervisor to provide
+ * correct protection for the page
+ */
+ if (add_mmu_update(xc_handle, mmu,
+ l2tab | MMU_EXTENDED_COMMAND, MMUEXT_PIN_L2_TABLE))
+ goto error_out;
+
+ for (count = 0; count < tot_pages; count++) {
+/*
+ DPRINTF (("add_mmu_update(0x%x, 0x%x, 0x%x, %d)\n", xc_handle, mmu,
+ (cpage_array[count]
+ << PAGE_SHIFT) |
+ MMU_MACHPHYS_UPDATE,
+ count));
+*/
+ if (add_mmu_update(xc_handle, mmu,
+ (cpage_array[count] << PAGE_SHIFT) |
+ MMU_MACHPHYS_UPDATE, count))
+ goto error_out;
+ //DPRINTF(("Do the next one\n"));
+ }
+/*
+ */
+
+ //dumpit(pm_handle, 3, 4, page_array);
+ /* put the virt_startinfo_addr at KZERO */
+ /* just hard-code for now */
+ *virt_startinfo_addr = 0x80000000;
+
+ DPRINTF(("virt_startinfo_addr = 0x%lx\n", *virt_startinfo_addr));
+ start_info = xc_map_foreign_range(xc_handle, dom,
+ PAGE_SIZE, PROT_READ | PROT_WRITE,
+ cpage_array[0]);
+ DPRINTF(("startinfo = 0x%p\n", start_info));
+ DPRINTF(("shared_info_frame is %lx\n", shared_info_frame));
+ memset(start_info, 0, sizeof (*start_info));
+ start_info->pt_base = 0x80000000 | cpu0pdb << PAGE_SHIFT;
+ start_info->mfn_list = 0x80000000 | (page_array_page) << PAGE_SHIFT;
+ DPRINTF(("mfn_list 0x%lx\n", start_info->mfn_list));
+ start_info->mod_start = 0;
+ start_info->mod_len = 0;
+ start_info->nr_pages = tot_pte_pages * 1024;
+ start_info->nr_pt_frames = tot_pte_pages + 1;
+ start_info->shared_info = shared_info_frame;
+ start_info->flags = 0;
+ DPRINTF((" control event channel is %d\n", control_evtchn));
+ start_info->domain_controller_evtchn = control_evtchn;
+ strncpy(start_info->cmd_line, cmdline, MAX_CMDLINE);
+ start_info->cmd_line[MAX_CMDLINE - 1] = '\0';
+ munmap(start_info, PAGE_SIZE);
+
+ DPRINTF(("done setting up start_info\n"));
+ DPRINTF(("shared_info_frame = 0x%lx\n", shared_info_frame));
+ /* shared_info page starts its life empty. */
+
+ shared_info = xc_map_foreign_range(xc_handle, dom,
+ PAGE_SIZE, PROT_READ | PROT_WRITE,
+ shared_info_frame);
+ memset(shared_info, 0, PAGE_SIZE);
+ /* Mask all upcalls... */
+ DPRINTF(("mask all upcalls\n"));
+ for (i = 0; i < MAX_VIRT_CPUS; i++)
+ shared_info->vcpu_data[i].evtchn_upcall_mask = 1;
+ munmap(shared_info, PAGE_SIZE);
+
+ /* Send the page update requests down to the hypervisor. */
+ DPRINTF(("send page update reqs down.\n"));
+ if (finish_mmu_updates(xc_handle, mmu))
+ goto error_out;
+
+ //DPRINTF (("call dumpit.\n"));
+ //dumpit(pm_handle, 0x100000>>12, tot_pages, page_array) ;
+ //dumpit (pm_handle, 2, 0x100, page_array);
+ free(mmu);
+
+ /* we don't bother freeing anything at this point --
+ * we're exiting and it is pointless
+ */
+ return 0;
+
+ error_out:
+ /* oh well we still free some things -- I oughtta nuke this */
+ if (mmu != NULL)
+ free(mmu);
+ ;
+ return -1;
+}
+
+int
+xc_plan9_build(int xc_handle,
+ u32 domid,
+ const char *image_name,
+ const char *cmdline,
+ unsigned int control_evtchn, unsigned long flags)
+{
+ dom0_op_t launch_op, op;
+ unsigned long load_addr;
+ long tot_pages;
+ int kernel_fd = -1;
+ gzFile kernel_gfd = NULL;
+ int rc, i;
+ full_execution_context_t st_ctxt, *ctxt = &st_ctxt;
+ unsigned long virt_startinfo_addr;
+
+ if ((tot_pages = xc_get_tot_pages(xc_handle, domid)) < 0) {
+ PERROR("Could not find total pages for domain");
+ return 1;
+ }
+ DPRINTF(("xc_get_tot_pages returns %ld pages\n", tot_pages));
+
+ kernel_fd = open(image_name, O_RDONLY);
+ if (kernel_fd < 0) {
+ PERROR("Could not open kernel image");
+ return 1;
+ }
+
+ if ((kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL) {
+ PERROR("Could not allocate decompression state for state file");
+ close(kernel_fd);
+ return 1;
+ }
+
+ DPRINTF(("xc_get_tot_pages returns %ld pages\n", tot_pages));
+ if (mlock(&st_ctxt, sizeof (st_ctxt))) {
+ PERROR("Unable to mlock ctxt");
+ return 1;
+ }
+
+ op.cmd = DOM0_GETDOMAININFO;
+ op.u.getdomaininfo.domain = (domid_t) domid;
+ op.u.getdomaininfo.exec_domain = 0;
+ op.u.getdomaininfo.ctxt = ctxt;
+ if ((do_dom0_op(xc_handle, &op) < 0) ||
+ ((u32) op.u.getdomaininfo.domain != domid)) {
+ PERROR("Could not get info on domain");
+ goto error_out;
+ }
+ DPRINTF(("xc_get_tot_pages returns %ld pages\n", tot_pages));
+
+ if (!(op.u.getdomaininfo.flags & DOMFLAGS_PAUSED)
+ || (op.u.getdomaininfo.ctxt->pt_base != 0)) {
+ ERROR("Domain is already constructed");
+ goto error_out;
+ }
+
+ DPRINTF(("xc_get_tot_pages returns %ld pages\n", tot_pages));
+ if (setup_guest(xc_handle, domid, kernel_gfd, tot_pages,
+ &virt_startinfo_addr,
+ &load_addr, &st_ctxt, cmdline,
+ op.u.getdomaininfo.shared_info_frame,
+ control_evtchn, flags) < 0) {
+ ERROR("Error constructing guest OS");
+ goto error_out;
+ }
+
+ /* leave the leak in here for now
+ if ( kernel_fd >= 0 )
+ close(kernel_fd);
+ if( kernel_gfd )
+ gzclose(kernel_gfd);
+ */
+ ctxt->flags = 0;
+
+ /*
+ * Initial register values:
+ * DS,ES,FS,GS = FLAT_KERNEL_DS
+ * CS:EIP = FLAT_KERNEL_CS:start_pc
+ * SS:ESP = FLAT_KERNEL_DS:start_stack
+ * ESI = start_info
+ * [EAX,EBX,ECX,EDX,EDI,EBP are zero]
+ * EFLAGS = IF | 2 (bit 1 is reserved and should always be 1)
+ */
+ ctxt->cpu_ctxt.ds = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.es = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.fs = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.gs = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.ss = FLAT_KERNEL_DS;
+ ctxt->cpu_ctxt.cs = FLAT_KERNEL_CS;
+ ctxt->cpu_ctxt.eip = load_addr;
+ ctxt->cpu_ctxt.eip = 0x80100020;
+ /* put stack at top of second page */
+ ctxt->cpu_ctxt.esp = 0x80000000 + (STACKPAGE << PAGE_SHIFT);
+
+ /* why is this set? */
+ ctxt->cpu_ctxt.esi = ctxt->cpu_ctxt.esp;
+ ctxt->cpu_ctxt.eflags = (1 << 9) | (1 << 2);
+
+ /* FPU is set up to default initial state. */
+ memset(ctxt->fpu_ctxt, 0, sizeof (ctxt->fpu_ctxt));
+
+ /* Virtual IDT is empty at start-of-day. */
+ for (i = 0; i < 256; i++) {
+ ctxt->trap_ctxt[i].vector = i;
+ ctxt->trap_ctxt[i].cs = FLAT_KERNEL_CS;
+ }
+ ctxt->fast_trap_idx = 0;
+
+ /* No LDT. */
+ ctxt->ldt_ents = 0;
+
+ /* Use the default Xen-provided GDT. */
+ ctxt->gdt_ents = 0;
+
+ /* Ring 1 stack is the initial stack. */
+ /* put stack at top of second page */
+ ctxt->kernel_ss = FLAT_KERNEL_DS;
+ ctxt->kernel_esp = ctxt->cpu_ctxt.esp;
+
+ /* No debugging. */
+ memset(ctxt->debugreg, 0, sizeof (ctxt->debugreg));
+
+ /* No callback handlers. */
+ ctxt->event_callback_cs = FLAT_KERNEL_CS;
+ ctxt->event_callback_eip = 0;
+ ctxt->failsafe_callback_cs = FLAT_KERNEL_CS;
+ ctxt->failsafe_callback_eip = 0;
+
+ memset(&launch_op, 0, sizeof (launch_op));
+
+ launch_op.u.builddomain.domain = (domid_t) domid;
+ // launch_op.u.builddomain.num_vifs = 1;
+ launch_op.u.builddomain.ctxt = ctxt;
+ launch_op.cmd = DOM0_BUILDDOMAIN;
+ rc = do_dom0_op(xc_handle, &launch_op);
+
+ fprintf(stderr, "RC is %d\n", rc);
+ return rc;
+
+ error_out:
+ if (kernel_fd >= 0)
+ close(kernel_fd);
+ if (kernel_gfd)
+ gzclose(kernel_gfd);
+
+ return -1;
+}
+
+/*
+ * Plan 9 memory layout (initial)
+ * ----------------
+ * | info from xen| @0
+ * ----------------
+ * | stack |
+ * ----------------<--- page 2
+ * | empty |
+ * ---------------<---- page 5 MACHADDR (always points to machp[cpuno]
+ * | aliased |
+ * ---------------<----- page 6 CPU0MACH
+ * | CPU0MACH |
+ * ----------------
+ * | empty |
+ * ---------------- *virt_load_addr = ehdr.e_entry (0x80100000)
+ * | kernel |
+ * | |
+ * ---------------- <----- page aligned boundary.
+ * | data |
+ * | |
+ * ----------------
+ * | bss |
+ * ----------------<--- end of kernel (page aligned)
+ * | PMD cpu0pdb |
+ * ----------------<--- page +1
+ * | PTE cpu0pte |
+ * ----------------<--- page (tot_pte_pages)/1024
+ * | page_array |
+ * ---------------- <--- page (tot_pte_pages)/1024
+ * | empty to TOM |
+ * ----------------
+ */
+
+static int
+loadp9image(gzFile kernel_gfd, int xc_handle, u32 dom,
+ unsigned long *page_array,
+ unsigned long tot_pages, unsigned long *virt_load_addr,
+ unsigned long *ksize, unsigned long *symtab_addr,
+ unsigned long *symtab_len,
+ unsigned long *first_data_page, unsigned long *pdb_page)
+{
+ unsigned long datapage;
+ Exec ehdr;
+
+ char *p;
+ unsigned long maxva;
+ int curpos, ret;
+ PAGE *image = 0;
+ unsigned long image_tot_pages = 0;
+ unsigned long textround;
+
+ ret = -1;
+
+ p = NULL;
+ maxva = 0;
+
+ if (gzread(kernel_gfd, &ehdr, sizeof (Exec)) != sizeof (Exec)) {
+ PERROR("Error reading kernel image P9 header.");
+ goto out;
+ }
+
+ plan9header(&ehdr);
+ curpos = sizeof (Exec);
+
+ if (ehdr.magic != I_MAGIC) {
+ PERROR("Image does not have an P9 header.");
+ goto out;
+ }
+
+ textround = ((ehdr.text + 0x20 + 4095) >> 12) << 12;
+ *first_data_page = 0x100000 + textround;
+ DPRINTF(("ehrd.text is 0x%lx, textround is 0x%lx\n",
+ ehdr.text, textround));
+
+ image_tot_pages =
+ (textround + ehdr.data + ehdr.bss + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ DPRINTF(("tot pages is %ld\n", image_tot_pages));
+
+ *virt_load_addr = 0x80100000;
+
+ if ((*virt_load_addr & (PAGE_SIZE - 1)) != 0) {
+ ERROR("We can only deal with page-aligned load addresses");
+ goto out;
+ }
+
+ if ((*virt_load_addr + (image_tot_pages << PAGE_SHIFT)) >
+ HYPERVISOR_VIRT_START) {
+ ERROR("Cannot map all domain memory without hitting Xen space");
+ goto out;
+ }
+
+ /* just malloc an image that is image_tot_pages in size. Then read in
+ * the image -- text, data, -- to page-rounded alignments.
+ * then copy into xen .
+ * this gets BSS zeroed for free
+ */
+ DPRINTF(("Allocate %ld bytes\n", image_tot_pages * sizeof (*image)));
+ image = calloc(image_tot_pages, sizeof (*image));
+ if (!image)
+ return blah("alloc data");
+ /* text starts at 0x20, after the header, just like Unix long ago */
+ if (gzread(kernel_gfd, &image[0].data[sizeof (Exec)], ehdr.text) <
+ ehdr.text)
+ return blah("read text");
+ DPRINTF(("READ TEXT %ld bytes\n", ehdr.text));
+ datapage = ((ehdr.text + sizeof (Exec)) / PAGE_SIZE) + 1;
+ if (gzread(kernel_gfd, image[datapage].data, ehdr.data) < ehdr.data)
+ return blah("read data");
+ DPRINTF(("READ DATA %ld bytes\n", ehdr.data));
+
+ /* nice contig stuff */
+ /* oops need to start at 0x100000 */
+
+ ret = memcpy_toguest(xc_handle, dom,
+ image, image_tot_pages * 4096, page_array, 0x100);
+ DPRINTF(("done copying kernel to guest memory\n"));
+
+ out:
+ if (image)
+ free(image);
+ *pdb_page = image_tot_pages + (0x100000 >> PAGE_SHIFT);
+ return ret;
+}
diff --git a/tools/libxc/xc_private.c b/tools/libxc/xc_private.c
index 8807f8a9bf..65aa1085e0 100644
--- a/tools/libxc/xc_private.c
+++ b/tools/libxc/xc_private.c
@@ -4,136 +4,10 @@
* Helper functions for the rest of the library.
*/
+#include <zlib.h>
#include "xc_private.h"
-#define MAX_EXTENTS 8
-typedef struct {
- int fd;
- struct {
- void *base;
- unsigned long length;
- } extent[MAX_EXTENTS];
-} mapper_desc_t;
-
-void *init_pfn_mapper(domid_t domid)
-{
- int fd = open("/dev/mem", O_RDWR);
- mapper_desc_t *desc;
-
- if ( fd < 0 )
- return NULL;
-
- if ( (desc = malloc(sizeof(*desc))) == NULL )
- {
- close(fd);
- return NULL;
- }
-
- (void)ioctl(fd, _IO('M', 1), (unsigned long)domid);
-
- memset(desc, 0, sizeof(*desc));
- desc->fd = fd;
-
- return desc;
-}
-
-int close_pfn_mapper(void *pm_handle)
-{
- mapper_desc_t *desc = pm_handle;
- int i;
-
- for ( i = 0; i < MAX_EXTENTS; i++ )
- {
- if ( desc->extent[i].base != NULL )
- (void)munmap(desc->extent[i].base, desc->extent[i].length);
- }
-
- close(desc->fd);
- free(desc);
-
- return 0;
-}
-
-static int get_free_offset(mapper_desc_t *desc)
-{
- int i;
-
- for ( i = 0; i < MAX_EXTENTS; i++ )
- {
- if ( desc->extent[i].base == NULL )
- break;
- }
-
- if ( i == MAX_EXTENTS )
- {
- fprintf(stderr, "Extent overflow in map_pfn_*()!\n");
- fflush(stderr);
- *(int*)0=0; /* XXX */
- }
-
- return i;
-}
-
-void *map_pfn_writeable(void *pm_handle, unsigned long pfn)
-{
- mapper_desc_t *desc = pm_handle;
- void *vaddr;
- int off;
-
- vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
- MAP_SHARED, desc->fd, pfn << PAGE_SHIFT);
- if ( vaddr == MAP_FAILED )
- return NULL;
-
- off = get_free_offset(desc);
- desc->extent[off].base = vaddr;
- desc->extent[off].length = PAGE_SIZE;
-
- return vaddr;
-}
-
-void *map_pfn_readonly(void *pm_handle, unsigned long pfn)
-{
- mapper_desc_t *desc = pm_handle;
- void *vaddr;
- int off;
-
- vaddr = mmap(NULL, PAGE_SIZE, PROT_READ,
- MAP_SHARED, desc->fd, pfn << PAGE_SHIFT);
- if ( vaddr == MAP_FAILED )
- return NULL;
-
- off = get_free_offset(desc);
- desc->extent[off].base = vaddr;
- desc->extent[off].length = PAGE_SIZE;
-
- return vaddr;
-}
-
-void unmap_pfn(void *pm_handle, void *vaddr)
-{
- mapper_desc_t *desc = pm_handle;
- int i;
- unsigned long len = 0;
-
- for ( i = 0; i < MAX_EXTENTS; i++ )
- {
- if ( desc->extent[i].base == vaddr )
- {
- desc->extent[i].base = NULL;
- len = desc->extent[i].length;
- }
- }
-
- if ( len == 0 )
- *(int*)0 = 0; /* XXX */
-
- (void)munmap(vaddr, len);
-}
-
-/*******************/
-
-void *mfn_mapper_map_batch(int xc_handle, domid_t dom, int prot,
+void *xc_map_foreign_batch(int xc_handle, u32 dom, int prot,
unsigned long *arr, int num )
{
privcmd_mmapbatch_t ioctlx;
@@ -158,7 +32,7 @@ void *mfn_mapper_map_batch(int xc_handle, domid_t dom, int prot,
/*******************/
-void *mfn_mapper_map_single(int xc_handle, domid_t dom,
+void *xc_map_foreign_range(int xc_handle, u32 dom,
int size, int prot,
unsigned long mfn )
{
@@ -228,11 +102,9 @@ static int flush_mmu_updates(int xc_handle, mmu_t *mmu)
if ( mmu->idx == FIRST_MMU_UPDATE )
return 0;
- /* The first two requests set the correct subject domain (PTS and GPS). */
- mmu->updates[0].val = (unsigned long)(mmu->subject<<16) & ~0xFFFFUL;
- mmu->updates[0].ptr = (unsigned long)(mmu->subject<< 0) & ~0xFFFFUL;
- mmu->updates[0].ptr |= MMU_EXTENDED_COMMAND;
- mmu->updates[0].val |= MMUEXT_SET_SUBJECTDOM | SET_PAGETABLE_SUBJECTDOM;
+ mmu->updates[0].ptr = MMU_EXTENDED_COMMAND;
+ mmu->updates[0].val = MMUEXT_SET_FOREIGNDOM;
+ mmu->updates[0].val |= (unsigned long)mmu->subject << 16;
hypercall.op = __HYPERVISOR_mmu_update;
hypercall.arg[0] = (unsigned long)mmu->updates;
@@ -288,15 +160,16 @@ int finish_mmu_updates(int xc_handle, mmu_t *mmu)
}
-long long xc_domain_get_cpu_usage( int xc_handle, domid_t domid )
+long long xc_domain_get_cpu_usage( int xc_handle, domid_t domid, int vcpu )
{
dom0_op_t op;
op.cmd = DOM0_GETDOMAININFO;
op.u.getdomaininfo.domain = (domid_t)domid;
+ op.u.getdomaininfo.exec_domain = (u16)vcpu;
op.u.getdomaininfo.ctxt = NULL;
if ( (do_dom0_op(xc_handle, &op) < 0) ||
- ((u32)op.u.getdomaininfo.domain != domid) )
+ ((u16)op.u.getdomaininfo.domain != domid) )
{
PERROR("Could not get info on domain");
return -1;
@@ -317,3 +190,164 @@ unsigned long csum_page (void * page)
return sum ^ (sum>>32);
}
+
+unsigned long xc_get_m2p_start_mfn ( int xc_handle )
+{
+ unsigned long mfn;
+
+ if ( ioctl( xc_handle, IOCTL_PRIVCMD_GET_MACH2PHYS_START_MFN, &mfn ) < 0 )
+ {
+ perror("xc_get_m2p_start_mfn:");
+ return 0;
+ }
+ return mfn;
+}
+
+int xc_get_pfn_list(int xc_handle,
+ u32 domid,
+ unsigned long *pfn_buf,
+ unsigned long max_pfns)
+{
+ dom0_op_t op;
+ int ret;
+ op.cmd = DOM0_GETMEMLIST;
+ op.u.getmemlist.domain = (domid_t)domid;
+ op.u.getmemlist.max_pfns = max_pfns;
+ op.u.getmemlist.buffer = pfn_buf;
+
+
+ if ( mlock(pfn_buf, max_pfns * sizeof(unsigned long)) != 0 )
+ {
+ PERROR("Could not lock pfn list buffer");
+ return -1;
+ }
+
+ ret = do_dom0_op(xc_handle, &op);
+
+ (void)munlock(pfn_buf, max_pfns * sizeof(unsigned long));
+
+#if 0
+#ifdef DEBUG
+ DPRINTF(("Ret for xc_get_pfn_list is %d\n", ret));
+ if (ret >= 0) {
+ int i, j;
+ for (i = 0; i < op.u.getmemlist.num_pfns; i += 16) {
+ fprintf(stderr, "0x%x: ", i);
+ for (j = 0; j < 16; j++)
+ fprintf(stderr, "0x%lx ", pfn_buf[i + j]);
+ fprintf(stderr, "\n");
+ }
+ }
+#endif
+#endif
+
+ return (ret < 0) ? -1 : op.u.getmemlist.num_pfns;
+}
+
+long xc_get_tot_pages(int xc_handle, u32 domid)
+{
+ dom0_op_t op;
+ op.cmd = DOM0_GETDOMAININFO;
+ op.u.getdomaininfo.domain = (domid_t)domid;
+ op.u.getdomaininfo.exec_domain = 0;
+ op.u.getdomaininfo.ctxt = NULL;
+ return (do_dom0_op(xc_handle, &op) < 0) ?
+ -1 : op.u.getdomaininfo.tot_pages;
+}
+
+int xc_copy_to_domain_page(int xc_handle,
+ u32 domid,
+ unsigned long dst_pfn,
+ void *src_page)
+{
+ void *vaddr = xc_map_foreign_range(
+ xc_handle, domid, PAGE_SIZE, PROT_WRITE, dst_pfn);
+ if ( vaddr == NULL )
+ return -1;
+ memcpy(vaddr, src_page, PAGE_SIZE);
+ munmap(vaddr, PAGE_SIZE);
+ return 0;
+}
+
+unsigned long xc_get_filesz(int fd)
+{
+ u16 sig;
+ u32 _sz = 0;
+ unsigned long sz;
+
+ lseek(fd, 0, SEEK_SET);
+ read(fd, &sig, sizeof(sig));
+ sz = lseek(fd, 0, SEEK_END);
+ if ( sig == 0x8b1f ) /* GZIP signature? */
+ {
+ lseek(fd, -4, SEEK_END);
+ read(fd, &_sz, 4);
+ sz = _sz;
+ }
+ lseek(fd, 0, SEEK_SET);
+
+ return sz;
+}
+
+char *xc_read_kernel_image(const char *filename, unsigned long *size)
+{
+ int kernel_fd = -1;
+ gzFile kernel_gfd = NULL;
+ char *image = NULL;
+ unsigned int bytes;
+
+ if ( (kernel_fd = open(filename, O_RDONLY)) < 0 )
+ {
+ PERROR("Could not open kernel image");
+ goto out;
+ }
+
+ *size = xc_get_filesz(kernel_fd);
+
+ if ( (kernel_gfd = gzdopen(kernel_fd, "rb")) == NULL )
+ {
+ PERROR("Could not allocate decompression state for state file");
+ goto out;
+ }
+
+ if ( (image = malloc(*size)) == NULL )
+ {
+ PERROR("Could not allocate memory for kernel image");
+ goto out;
+ }
+
+ if ( (bytes = gzread(kernel_gfd, image, *size)) != *size )
+ {
+ PERROR("Error reading kernel image, could not"
+ " read the whole image (%d != %ld).", bytes, *size);
+ free(image);
+ image = NULL;
+ }
+
+ out:
+ if ( kernel_gfd != NULL )
+ gzclose(kernel_gfd);
+ else if ( kernel_fd >= 0 )
+ close(kernel_fd);
+ return image;
+}
+
+void xc_map_memcpy(unsigned long dst, char *src, unsigned long size,
+ int xch, u32 dom, unsigned long *parray,
+ unsigned long vstart)
+{
+ char *va;
+ unsigned long chunksz, done, pa;
+
+ for ( done = 0; done < size; done += chunksz )
+ {
+ pa = dst + done - vstart;
+ va = xc_map_foreign_range(
+ xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
+ chunksz = size - done;
+ if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
+ chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
+ memcpy(va + (pa & (PAGE_SIZE-1)), src + done, chunksz);
+ munmap(va, PAGE_SIZE);
+ }
+}
diff --git a/tools/libxc/xc_private.h b/tools/libxc/xc_private.h
index 5a1cc1d122..0aed21274e 100644
--- a/tools/libxc/xc_private.h
+++ b/tools/libxc/xc_private.h
@@ -16,15 +16,7 @@
#include "xc.h"
-/* from xen/include/hypervisor-ifs */
-#include <hypervisor-if.h>
-#include <dom0_ops.h>
-#include <event_channel.h>
-#include <sched_ctl.h>
-#include <io/domain_controller.h>
-
-#include <asm-xen/proc_cmd.h>
-
+#include <xen/linux/privcmd.h>
#define _PAGE_PRESENT 0x001
#define _PAGE_RW 0x002
@@ -106,35 +98,44 @@ static inline int do_dom0_op(int xc_handle, dom0_op_t *op)
out1: return ret;
}
-static inline int do_multicall_op(int xc_handle,
- void *call_list, int nr_calls)
+static inline int do_dom_mem_op(int xc_handle,
+ unsigned int memop,
+ unsigned int *extent_list,
+ unsigned int nr_extents,
+ unsigned int extent_order,
+ domid_t domid)
{
- int ret = -1;
privcmd_hypercall_t hypercall;
-
- hypercall.op = __HYPERVISOR_multicall;
- hypercall.arg[0] = (unsigned long)call_list;
- hypercall.arg[1] = (unsigned long)nr_calls;
+ long ret = -EINVAL;
+
+ hypercall.op = __HYPERVISOR_dom_mem_op;
+ hypercall.arg[0] = (unsigned long)memop;
+ hypercall.arg[1] = (unsigned long)extent_list;
+ hypercall.arg[2] = (unsigned long)nr_extents;
+ hypercall.arg[3] = (unsigned long)extent_order;
+ hypercall.arg[4] = (unsigned long)domid;
+
+ if ( mlock(extent_list, nr_extents*sizeof(unsigned long)) != 0 )
+ {
+ PERROR("Could not lock memory for Xen hypercall");
+ goto out1;
+ }
if ( (ret = do_xen_hypercall(xc_handle, &hypercall)) < 0 )
{
- if ( errno == EACCES )
- fprintf(stderr, "Dom0 operation failed -- need to"
- " rebuild the user-space tool set?\n");
- goto out1;
+ fprintf(stderr, "Dom_mem operation failed (rc=%ld errno=%d)-- need to"
+ " rebuild the user-space tool set?\n",ret,errno);
+ goto out2;
}
+ out2: (void)munlock(extent_list, nr_extents*sizeof(unsigned long));
out1: return ret;
-}
+}
+
/*
* PFN mapping.
*/
-void *init_pfn_mapper(domid_t domid);
-int close_pfn_mapper(void *pm_handle);
-void *map_pfn_writeable(void *pm_handle, unsigned long pfn);
-void *map_pfn_readonly(void *pm_handle, unsigned long pfn);
-void unmap_pfn(void *pm_handle, void *vaddr);
int get_pfn_type_batch(int xc_handle, u32 dom, int num, unsigned long *arr);
unsigned long csum_page (void * page);
@@ -184,29 +185,21 @@ typedef struct mfn_mapper {
} mfn_mapper_t;
-void * mfn_mapper_map_single(int xc_handle, domid_t dom, int size, int prot,
- unsigned long mfn );
-
-void * mfn_mapper_map_batch(int xc_handle, domid_t dom, int prot,
- unsigned long *arr, int num );
-
-mfn_mapper_t * mfn_mapper_init(int xc_handle, domid_t dom, int size, int prot);
+#include "xc_io.h"
-void * mfn_mapper_base(mfn_mapper_t *t);
+unsigned long xc_get_m2p_start_mfn ( int xc_handle );
-void mfn_mapper_close(mfn_mapper_t *t);
+long xc_get_tot_pages(int xc_handle, u32 domid);
-int mfn_mapper_flush_queue(mfn_mapper_t *t);
+int xc_copy_to_domain_page(int xc_handle, u32 domid,
+ unsigned long dst_pfn, void *src_page);
-void * mfn_mapper_queue_entry(mfn_mapper_t *t, int offset,
- unsigned long mfn, int size );
+unsigned long xc_get_filesz(int fd);
-long long xc_domain_get_cpu_usage( int xc_handle, domid_t domid );
+char *xc_read_kernel_image(const char *filename, unsigned long *size);
-#include "xc_io.h"
+void xc_map_memcpy(unsigned long dst, char *src, unsigned long size,
+ int xch, u32 dom, unsigned long *parray,
+ unsigned long vstart);
-int xc_domain_getfullinfo(int xc_handle,
- u32 domid,
- dom0_op_t *op,
- full_execution_context_t *ctxt );
#endif /* __XC_PRIVATE_H__ */
diff --git a/tools/libxc/xc_vmx_build.c b/tools/libxc/xc_vmx_build.c
new file mode 100644
index 0000000000..c286febdf0
--- /dev/null
+++ b/tools/libxc/xc_vmx_build.c
@@ -0,0 +1,802 @@
+/******************************************************************************
+ * xc_vmx_build.c
+ */
+
+#include "xc_private.h"
+#define ELFSIZE 32
+#include "xc_elf.h"
+#include <stdlib.h>
+#include <zlib.h>
+#include "linux_boot_params.h"
+
+#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED)
+#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_ACCESSED|_PAGE_DIRTY|_PAGE_USER)
+
+#define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
+#define round_pgdown(_p) ((_p)&PAGE_MASK)
+
+#define LINUX_BOOT_PARAMS_ADDR 0x00090000
+#define LINUX_KERNEL_ENTR_ADDR 0x00100000
+#define LINUX_PAGE_OFFSET 0xC0000000
+
+struct domain_setup_info
+{
+ unsigned long v_start;
+ unsigned long v_end;
+ unsigned long v_kernstart;
+ unsigned long v_kernend;
+ unsigned long v_kernentry;
+
+ unsigned int use_writable_pagetables;
+ unsigned int load_bsd_symtab;
+
+ unsigned long symtab_addr;
+ unsigned long symtab_len;
+};
+
+static int
+parseelfimage(
+ char *elfbase, unsigned long elfsize, struct domain_setup_info *dsi);
+static int
+loadelfimage(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ unsigned long vstart);
+static int
+loadelfsymtab(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ struct domain_setup_info *dsi);
+
+static void build_e820map(struct mem_map *mem_mapp, unsigned long mem_size)
+{
+ int nr_map = 0;
+
+ /* XXX: Doesn't work for > 4GB yet */
+ mem_mapp->map[0].addr = 0x0;
+ mem_mapp->map[0].size = 0x9F800;
+ mem_mapp->map[0].type = E820_RAM;
+ mem_mapp->map[0].caching_attr = MEMMAP_WB;
+ nr_map++;
+
+ mem_mapp->map[1].addr = 0x9F800;
+ mem_mapp->map[1].size = 0x800;
+ mem_mapp->map[1].type = E820_RESERVED;
+ mem_mapp->map[1].caching_attr = MEMMAP_UC;
+ nr_map++;
+
+ mem_mapp->map[2].addr = 0xA0000;
+ mem_mapp->map[2].size = 0x20000;
+ mem_mapp->map[2].type = E820_IO;
+ mem_mapp->map[2].caching_attr = MEMMAP_UC;
+ nr_map++;
+
+ mem_mapp->map[3].addr = 0xF0000;
+ mem_mapp->map[3].size = 0x10000;
+ mem_mapp->map[3].type = E820_RESERVED;
+ mem_mapp->map[3].caching_attr = MEMMAP_UC;
+ nr_map++;
+
+ mem_mapp->map[4].addr = 0x100000;
+ mem_mapp->map[4].size = mem_size - 0x100000 - PAGE_SIZE;
+ mem_mapp->map[4].type = E820_RAM;
+ mem_mapp->map[4].caching_attr = MEMMAP_WB;
+ nr_map++;
+
+ mem_mapp->map[5].addr = mem_size - PAGE_SIZE;
+ mem_mapp->map[5].size = PAGE_SIZE;
+ mem_mapp->map[5].type = E820_SHARED;
+ mem_mapp->map[5].caching_attr = MEMMAP_WB;
+ nr_map++;
+
+ mem_mapp->map[6].addr = mem_size;
+ mem_mapp->map[6].size = 0x3 * PAGE_SIZE;
+ mem_mapp->map[6].type = E820_NVS;
+ mem_mapp->map[6].caching_attr = MEMMAP_UC;
+ nr_map++;
+
+ mem_mapp->map[7].addr = mem_size + 0x3 * PAGE_SIZE;
+ mem_mapp->map[7].size = 0xA * PAGE_SIZE;
+ mem_mapp->map[7].type = E820_ACPI;
+ mem_mapp->map[7].caching_attr = MEMMAP_WB;
+ nr_map++;
+
+ mem_mapp->map[8].addr = 0xFEC00000;
+ mem_mapp->map[8].size = 0x1400000;
+ mem_mapp->map[8].type = E820_IO;
+ mem_mapp->map[8].caching_attr = MEMMAP_UC;
+ nr_map++;
+
+ mem_mapp->nr_map = nr_map;
+}
+
+static int setup_guest(int xc_handle,
+ u32 dom, int memsize,
+ char *image, unsigned long image_size,
+ gzFile initrd_gfd, unsigned long initrd_len,
+ unsigned long nr_pages,
+ full_execution_context_t *ctxt,
+ const char *cmdline,
+ unsigned long shared_info_frame,
+ unsigned int control_evtchn,
+ unsigned long flags,
+ struct mem_map * mem_mapp)
+{
+ l1_pgentry_t *vl1tab=NULL, *vl1e=NULL;
+ l2_pgentry_t *vl2tab=NULL, *vl2e=NULL;
+ unsigned long *page_array = NULL;
+ unsigned long l2tab;
+ unsigned long l1tab;
+ unsigned long count, i;
+ shared_info_t *shared_info;
+ struct linux_boot_params * boot_paramsp;
+ __u16 * boot_gdtp;
+ mmu_t *mmu = NULL;
+ int rc;
+
+ unsigned long nr_pt_pages;
+ unsigned long ppt_alloc;
+
+ struct domain_setup_info dsi;
+ unsigned long vinitrd_start;
+ unsigned long vinitrd_end;
+ unsigned long vboot_params_start;
+ unsigned long vboot_params_end;
+ unsigned long vboot_gdt_start;
+ unsigned long vboot_gdt_end;
+ unsigned long vpt_start;
+ unsigned long vpt_end;
+ unsigned long v_end;
+
+ memset(&dsi, 0, sizeof(struct domain_setup_info));
+
+ rc = parseelfimage(image, image_size, &dsi);
+ if ( rc != 0 )
+ goto error_out;
+
+ if (dsi.use_writable_pagetables)
+ xc_domain_setvmassist(xc_handle, dom, VMASST_CMD_enable,
+ VMASST_TYPE_writable_pagetables);
+
+ if (dsi.load_bsd_symtab)
+ loadelfsymtab(image, xc_handle, dom, NULL, &dsi);
+
+ if ( (dsi.v_start & (PAGE_SIZE-1)) != 0 )
+ {
+ PERROR("Guest OS must load to a page boundary.\n");
+ goto error_out;
+ }
+
+ /*
+ * Why do we need this? The number of page-table frames depends on the
+ * size of the bootstrap address space. But the size of the address space
+ * depends on the number of page-table frames (since each one is mapped
+ * read-only). We have a pair of simultaneous equations in two unknowns,
+ * which we solve by exhaustive search.
+ */
+ vboot_params_start = LINUX_BOOT_PARAMS_ADDR;
+ vboot_params_end = vboot_params_start + PAGE_SIZE;
+ vboot_gdt_start = vboot_params_end;
+ vboot_gdt_end = vboot_gdt_start + PAGE_SIZE;
+
+ /* memsize is in megabytes */
+ v_end = memsize << 20;
+ vinitrd_end = v_end - PAGE_SIZE; /* leaving the top 4k untouched for IO requests page use */
+ vinitrd_start = vinitrd_end - initrd_len;
+ vinitrd_start = vinitrd_start & (~(PAGE_SIZE - 1));
+
+ if(initrd_len == 0)
+ vinitrd_start = vinitrd_end = 0;
+
+ nr_pt_pages = 1 + ((memsize + 3) >> 2);
+ vpt_start = v_end;
+ vpt_end = vpt_start + (nr_pt_pages * PAGE_SIZE);
+
+ printf("VIRTUAL MEMORY ARRANGEMENT:\n"
+ " Boot_params: %08lx->%08lx\n"
+ " boot_gdt: %08lx->%08lx\n"
+ " Loaded kernel: %08lx->%08lx\n"
+ " Init. ramdisk: %08lx->%08lx\n"
+ " Page tables: %08lx->%08lx\n"
+ " TOTAL: %08lx->%08lx\n",
+ vboot_params_start, vboot_params_end,
+ vboot_gdt_start, vboot_gdt_end,
+ dsi.v_kernstart, dsi.v_kernend,
+ vinitrd_start, vinitrd_end,
+ vpt_start, vpt_end,
+ dsi.v_start, v_end);
+ printf(" ENTRY ADDRESS: %08lx\n", dsi.v_kernentry);
+ printf(" INITRD LENGTH: %08lx\n", initrd_len);
+
+ if ( (v_end - dsi.v_start) > (nr_pages * PAGE_SIZE) )
+ {
+ printf("Initial guest OS requires too much space\n"
+ "(%luMB is greater than %luMB limit)\n",
+ (v_end-dsi.v_start)>>20, (nr_pages<<PAGE_SHIFT)>>20);
+ goto error_out;
+ }
+
+ if ( (page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL )
+ {
+ PERROR("Could not allocate memory");
+ goto error_out;
+ }
+
+ if ( xc_get_pfn_list(xc_handle, dom, page_array, nr_pages) != nr_pages )
+ {
+ PERROR("Could not get the page frame list");
+ goto error_out;
+ }
+
+ loadelfimage(image, xc_handle, dom, page_array, dsi.v_start);
+
+ if (dsi.load_bsd_symtab)
+ loadelfsymtab(image, xc_handle, dom, page_array, &dsi);
+
+ /* Load the initial ramdisk image. */
+ if ( initrd_len != 0 )
+ {
+ for ( i = (vinitrd_start - dsi.v_start);
+ i < (vinitrd_end - dsi.v_start); i += PAGE_SIZE )
+ {
+ char page[PAGE_SIZE];
+ if ( gzread(initrd_gfd, page, PAGE_SIZE) == -1 )
+ {
+ PERROR("Error reading initrd image, could not");
+ goto error_out;
+ }
+ xc_copy_to_domain_page(xc_handle, dom,
+ page_array[i>>PAGE_SHIFT], page);
+ }
+ }
+
+ if ( (mmu = init_mmu_updates(xc_handle, dom)) == NULL )
+ goto error_out;
+
+ /* First allocate page for page dir. */
+ ppt_alloc = (vpt_start - dsi.v_start) >> PAGE_SHIFT;
+ l2tab = page_array[ppt_alloc++] << PAGE_SHIFT;
+ ctxt->pt_base = l2tab;
+
+ /* Initialise the page tables. */
+ if ( (vl2tab = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ l2tab >> PAGE_SHIFT)) == NULL )
+ goto error_out;
+ memset(vl2tab, 0, PAGE_SIZE);
+ vl2e = &vl2tab[l2_table_offset(dsi.v_start)];
+ for ( count = 0; count < ((v_end-dsi.v_start)>>PAGE_SHIFT); count++ )
+ {
+ if ( ((unsigned long)vl1e & (PAGE_SIZE-1)) == 0 )
+ {
+ l1tab = page_array[ppt_alloc++] << PAGE_SHIFT;
+ if ( vl1tab != NULL )
+ munmap(vl1tab, PAGE_SIZE);
+ if ( (vl1tab = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
+ PROT_READ|PROT_WRITE,
+ l1tab >> PAGE_SHIFT)) == NULL )
+ {
+ munmap(vl2tab, PAGE_SIZE);
+ goto error_out;
+ }
+ memset(vl1tab, 0, PAGE_SIZE);
+ vl1e = &vl1tab[l1_table_offset(dsi.v_start + (count<<PAGE_SHIFT))];
+ *vl2e++ = l1tab | L2_PROT;
+ }
+
+ *vl1e = (page_array[count] << PAGE_SHIFT) | L1_PROT;
+ vl1e++;
+ }
+ munmap(vl1tab, PAGE_SIZE);
+ munmap(vl2tab, PAGE_SIZE);
+
+ /*
+ * Pin down l2tab addr as page dir page - causes hypervisor to provide
+ * correct protection for the page
+ */
+ if ( add_mmu_update(xc_handle, mmu,
+ l2tab | MMU_EXTENDED_COMMAND, MMUEXT_PIN_L2_TABLE) )
+ goto error_out;
+
+ boot_paramsp = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE,
+ page_array[(vboot_params_start-dsi.v_start)>>PAGE_SHIFT]);
+ memset(boot_paramsp, 0, sizeof(*boot_paramsp));
+
+ strncpy(boot_paramsp->cmd_line, cmdline, 0x800);
+ boot_paramsp->cmd_line[0x800-1] = '\0';
+ boot_paramsp->cmd_line_ptr = ((unsigned long) vboot_params_start) + offsetof(struct linux_boot_params, cmd_line);
+
+ boot_paramsp->setup_sects = 0;
+ boot_paramsp->mount_root_rdonly = 1;
+ boot_paramsp->swapdev = 0x0;
+ boot_paramsp->ramdisk_flags = 0x0;
+ boot_paramsp->root_dev = 0x0; /* We must tell kernel root dev by kernel command line. */
+
+ /* we don't have a ps/2 mouse now.
+ * 0xAA means a aux mouse is there.
+ * See detect_auxiliary_port() in pc_keyb.c.
+ */
+ boot_paramsp->aux_device_info = 0x0;
+
+ boot_paramsp->header_magic[0] = 0x48; /* "H" */
+ boot_paramsp->header_magic[1] = 0x64; /* "d" */
+ boot_paramsp->header_magic[2] = 0x72; /* "r" */
+ boot_paramsp->header_magic[3] = 0x53; /* "S" */
+
+ boot_paramsp->protocol_version = 0x0203; /* 2.03 */
+ boot_paramsp->loader_type = 0x71; /* GRUB */
+ boot_paramsp->loader_flags = 0x1; /* loaded high */
+ boot_paramsp->code32_start = LINUX_KERNEL_ENTR_ADDR; /* 1MB */
+ boot_paramsp->initrd_start = vinitrd_start;
+ boot_paramsp->initrd_size = initrd_len;
+
+ i = ((memsize - 1) << 10) - 4;
+ boot_paramsp->alt_mem_k = i; /* alt_mem_k */
+ boot_paramsp->screen.overlap.ext_mem_k = i & 0xFFFF; /* ext_mem_k */
+
+ /*
+ * Stuff SCREAN_INFO
+ */
+ boot_paramsp->screen.info.orig_x = 0;
+ boot_paramsp->screen.info.orig_y = 0;
+ boot_paramsp->screen.info.orig_video_page = 8;
+ boot_paramsp->screen.info.orig_video_mode = 3;
+ boot_paramsp->screen.info.orig_video_cols = 80;
+ boot_paramsp->screen.info.orig_video_ega_bx = 0;
+ boot_paramsp->screen.info.orig_video_lines = 25;
+ boot_paramsp->screen.info.orig_video_isVGA = 1;
+ boot_paramsp->screen.info.orig_video_points = 0x0010;
+
+ /* seems we may NOT stuff boot_paramsp->apm_bios_info */
+ /* seems we may NOT stuff boot_paramsp->drive_info */
+ /* seems we may NOT stuff boot_paramsp->sys_desc_table */
+ *((unsigned short *) &boot_paramsp->drive_info.dummy[0]) = 800;
+ boot_paramsp->drive_info.dummy[2] = 4;
+ boot_paramsp->drive_info.dummy[14] = 32;
+
+ /* memsize is in megabytes */
+ build_e820map(mem_mapp, memsize << 20);
+ boot_paramsp->e820_map_nr = mem_mapp->nr_map;
+ for (i=0; i<mem_mapp->nr_map; i++) {
+ boot_paramsp->e820_map[i].addr = mem_mapp->map[i].addr;
+ boot_paramsp->e820_map[i].size = mem_mapp->map[i].size;
+ boot_paramsp->e820_map[i].type = mem_mapp->map[i].type;
+ }
+ munmap(boot_paramsp, PAGE_SIZE);
+
+ boot_gdtp = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE,
+ page_array[(vboot_gdt_start-dsi.v_start)>>PAGE_SHIFT]);
+ memset(boot_gdtp, 0, PAGE_SIZE);
+ boot_gdtp[12*4 + 0] = boot_gdtp[13*4 + 0] = 0xffff; /* limit */
+ boot_gdtp[12*4 + 1] = boot_gdtp[13*4 + 1] = 0x0000; /* base */
+ boot_gdtp[12*4 + 2] = 0x9a00; boot_gdtp[13*4 + 2] = 0x9200; /* perms */
+ boot_gdtp[12*4 + 3] = boot_gdtp[13*4 + 3] = 0x00cf; /* granu + top of limit */
+ munmap(boot_gdtp, PAGE_SIZE);
+
+ /* shared_info page starts its life empty. */
+ shared_info = xc_map_foreign_range(
+ xc_handle, dom, PAGE_SIZE, PROT_READ|PROT_WRITE, shared_info_frame);
+ memset(shared_info, 0, sizeof(shared_info_t));
+ /* Mask all upcalls... */
+ for ( i = 0; i < MAX_VIRT_CPUS; i++ )
+ shared_info->vcpu_data[i].evtchn_upcall_mask = 1;
+ munmap(shared_info, PAGE_SIZE);
+
+ /* Send the page update requests down to the hypervisor. */
+ if ( finish_mmu_updates(xc_handle, mmu) )
+ goto error_out;
+
+ free(mmu);
+ free(page_array);
+
+ /*
+ * Initial register values:
+ */
+ ctxt->cpu_ctxt.ds = 0x68;
+ ctxt->cpu_ctxt.es = 0x0;
+ ctxt->cpu_ctxt.fs = 0x0;
+ ctxt->cpu_ctxt.gs = 0x0;
+ ctxt->cpu_ctxt.ss = 0x68;
+ ctxt->cpu_ctxt.cs = 0x60;
+ ctxt->cpu_ctxt.eip = dsi.v_kernentry;
+ ctxt->cpu_ctxt.edx = vboot_gdt_start;
+ ctxt->cpu_ctxt.eax = 0x800;
+ ctxt->cpu_ctxt.esp = vboot_gdt_end;
+ ctxt->cpu_ctxt.ebx = 0; /* startup_32 expects this to be 0 to signal boot cpu */
+ ctxt->cpu_ctxt.ecx = mem_mapp->nr_map;
+ ctxt->cpu_ctxt.esi = vboot_params_start;
+ ctxt->cpu_ctxt.edi = vboot_params_start + 0x2d0;
+
+ ctxt->cpu_ctxt.eflags = (1<<2);
+
+ return 0;
+
+ error_out:
+ if ( mmu != NULL )
+ free(mmu);
+ if ( page_array != NULL )
+ free(page_array);
+ return -1;
+}
+
+
+#define VMX_FEATURE_FLAG 0x20
+
+int vmx_identify(void)
+{
+ int eax, ecx;
+
+ __asm__ __volatile__ ("cpuid"
+ : "=a" (eax), "=c" (ecx)
+ : "0" (1)
+ : "bx", "dx");
+ if (!(ecx & VMX_FEATURE_FLAG)) {
+ return -1;
+ }
+ return 0;
+}
+
+int xc_vmx_build(int xc_handle,
+ u32 domid,
+ int memsize,
+ const char *image_name,
+ struct mem_map *mem_mapp,
+ const char *ramdisk_name,
+ const char *cmdline,
+ unsigned int control_evtchn,
+ unsigned long flags)
+{
+ dom0_op_t launch_op, op;
+ int initrd_fd = -1;
+ gzFile initrd_gfd = NULL;
+ int rc, i;
+ full_execution_context_t st_ctxt, *ctxt = &st_ctxt;
+ unsigned long nr_pages;
+ char *image = NULL;
+ unsigned long image_size, initrd_size=0;
+
+ if ( vmx_identify() < 0 )
+ {
+ PERROR("CPU doesn't support VMX Extensions");
+ goto error_out;
+ }
+
+ if ( (nr_pages = xc_get_tot_pages(xc_handle, domid)) < 0 )
+ {
+ PERROR("Could not find total pages for domain");
+ goto error_out;
+ }
+
+ if ( (image = xc_read_kernel_image(image_name, &image_size)) == NULL )
+ goto error_out;
+
+ if ( (ramdisk_name != NULL) && (strlen(ramdisk_name) != 0) )
+ {
+ if ( (initrd_fd = open(ramdisk_name, O_RDONLY)) < 0 )
+ {
+ PERROR("Could not open the initial ramdisk image");
+ goto error_out;
+ }
+
+ initrd_size = xc_get_filesz(initrd_fd);
+
+ if ( (initrd_gfd = gzdopen(initrd_fd, "rb")) == NULL )
+ {
+ PERROR("Could not allocate decompression state for initrd");
+ goto error_out;
+ }
+ }
+
+ if ( mlock(&st_ctxt, sizeof(st_ctxt) ) )
+ {
+ PERROR("Unable to mlock ctxt");
+ return 1;
+ }
+
+ op.cmd = DOM0_GETDOMAININFO;
+ op.u.getdomaininfo.domain = (domid_t)domid;
+ op.u.getdomaininfo.exec_domain = 0;
+ op.u.getdomaininfo.ctxt = ctxt;
+ if ( (do_dom0_op(xc_handle, &op) < 0) ||
+ ((u16)op.u.getdomaininfo.domain != domid) )
+ {
+ PERROR("Could not get info on domain");
+ goto error_out;
+ }
+ if ( !(op.u.getdomaininfo.flags & DOMFLAGS_PAUSED) ||
+ (ctxt->pt_base != 0) )
+ {
+ ERROR("Domain is already constructed");
+ goto error_out;
+ }
+
+ if ( setup_guest(xc_handle, domid, memsize, image, image_size,
+ initrd_gfd, initrd_size, nr_pages,
+ ctxt, cmdline,
+ op.u.getdomaininfo.shared_info_frame,
+ control_evtchn, flags, mem_mapp) < 0 )
+ {
+ ERROR("Error constructing guest OS");
+ goto error_out;
+ }
+
+ if ( initrd_fd >= 0 )
+ close(initrd_fd);
+ if ( initrd_gfd )
+ gzclose(initrd_gfd);
+ if ( image != NULL )
+ free(image);
+
+ ctxt->flags = ECF_VMX_GUEST;
+ /* FPU is set up to default initial state. */
+ memset(ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt));
+
+ /* Virtual IDT is empty at start-of-day. */
+ for ( i = 0; i < 256; i++ )
+ {
+ ctxt->trap_ctxt[i].vector = i;
+ ctxt->trap_ctxt[i].cs = FLAT_KERNEL_CS;
+ }
+ ctxt->fast_trap_idx = 0;
+
+ /* No LDT. */
+ ctxt->ldt_ents = 0;
+
+ /* Use the default Xen-provided GDT. */
+ ctxt->gdt_ents = 0;
+
+ /* Ring 1 stack is the initial stack. */
+/*
+ ctxt->kernel_ss = FLAT_KERNEL_DS;
+ ctxt->kernel_esp = vstartinfo_start;
+*/
+ /* No debugging. */
+ memset(ctxt->debugreg, 0, sizeof(ctxt->debugreg));
+
+ /* No callback handlers. */
+ ctxt->event_callback_cs = FLAT_KERNEL_CS;
+ ctxt->event_callback_eip = 0;
+ ctxt->failsafe_callback_cs = FLAT_KERNEL_CS;
+ ctxt->failsafe_callback_eip = 0;
+
+ memset( &launch_op, 0, sizeof(launch_op) );
+
+ launch_op.u.builddomain.domain = (domid_t)domid;
+ launch_op.u.builddomain.ctxt = ctxt;
+
+ launch_op.cmd = DOM0_BUILDDOMAIN;
+ rc = do_dom0_op(xc_handle, &launch_op);
+ return rc;
+
+ error_out:
+ if ( initrd_gfd != NULL )
+ gzclose(initrd_gfd);
+ else if ( initrd_fd >= 0 )
+ close(initrd_fd);
+ if ( image != NULL )
+ free(image);
+
+ return -1;
+}
+
+static inline int is_loadable_phdr(Elf_Phdr *phdr)
+{
+ return ((phdr->p_type == PT_LOAD) &&
+ ((phdr->p_flags & (PF_W|PF_X)) != 0));
+}
+
+static int parseelfimage(char *elfbase,
+ unsigned long elfsize,
+ struct domain_setup_info *dsi)
+{
+ Elf_Ehdr *ehdr = (Elf_Ehdr *)elfbase;
+ Elf_Phdr *phdr;
+ Elf_Shdr *shdr;
+ unsigned long kernstart = ~0UL, kernend=0UL;
+ char *shstrtab;
+ int h;
+
+ if ( !IS_ELF(*ehdr) )
+ {
+ ERROR("Kernel image does not have an ELF header.");
+ return -EINVAL;
+ }
+
+ if ( (ehdr->e_phoff + (ehdr->e_phnum * ehdr->e_phentsize)) > elfsize )
+ {
+ ERROR("ELF program headers extend beyond end of image.");
+ return -EINVAL;
+ }
+
+ if ( (ehdr->e_shoff + (ehdr->e_shnum * ehdr->e_shentsize)) > elfsize )
+ {
+ ERROR("ELF section headers extend beyond end of image.");
+ return -EINVAL;
+ }
+
+ /* Find the section-header strings table. */
+ if ( ehdr->e_shstrndx == SHN_UNDEF )
+ {
+ ERROR("ELF image has no section-header strings table (shstrtab).");
+ return -EINVAL;
+ }
+ shdr = (Elf_Shdr *)(elfbase + ehdr->e_shoff +
+ (ehdr->e_shstrndx*ehdr->e_shentsize));
+ shstrtab = elfbase + shdr->sh_offset;
+
+ for ( h = 0; h < ehdr->e_phnum; h++ )
+ {
+ phdr = (Elf_Phdr *)(elfbase + ehdr->e_phoff + (h*ehdr->e_phentsize));
+ if ( !is_loadable_phdr(phdr) )
+ continue;
+ if ( phdr->p_paddr < kernstart )
+ kernstart = phdr->p_paddr;
+ if ( (phdr->p_paddr + phdr->p_memsz) > kernend )
+ kernend = phdr->p_paddr + phdr->p_memsz;
+ }
+
+ if ( (kernstart > kernend) ||
+ (ehdr->e_entry < kernstart) ||
+ (ehdr->e_entry > kernend) )
+ {
+ ERROR("Malformed ELF image.");
+ return -EINVAL;
+ }
+
+ dsi->v_start = 0x00000000;
+ dsi->use_writable_pagetables = 0;
+ dsi->load_bsd_symtab = 0;
+
+ dsi->v_kernstart = kernstart - LINUX_PAGE_OFFSET;
+ dsi->v_kernend = kernend - LINUX_PAGE_OFFSET;
+ dsi->v_kernentry = LINUX_KERNEL_ENTR_ADDR;
+
+ dsi->v_end = dsi->v_kernend;
+
+ return 0;
+}
+
+static int
+loadelfimage(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ unsigned long vstart)
+{
+ Elf_Ehdr *ehdr = (Elf_Ehdr *)elfbase;
+ Elf_Phdr *phdr;
+ int h;
+
+ char *va;
+ unsigned long pa, done, chunksz;
+
+ for ( h = 0; h < ehdr->e_phnum; h++ )
+ {
+ phdr = (Elf_Phdr *)(elfbase + ehdr->e_phoff + (h*ehdr->e_phentsize));
+ if ( !is_loadable_phdr(phdr) )
+ continue;
+
+ for ( done = 0; done < phdr->p_filesz; done += chunksz )
+ {
+ pa = (phdr->p_paddr + done) - vstart - LINUX_PAGE_OFFSET;
+ va = xc_map_foreign_range(
+ xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
+ chunksz = phdr->p_filesz - done;
+ if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
+ chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
+ memcpy(va + (pa & (PAGE_SIZE-1)),
+ elfbase + phdr->p_offset + done, chunksz);
+ munmap(va, PAGE_SIZE);
+ }
+
+ for ( ; done < phdr->p_memsz; done += chunksz )
+ {
+ pa = (phdr->p_paddr + done) - vstart - LINUX_PAGE_OFFSET;
+ va = xc_map_foreign_range(
+ xch, dom, PAGE_SIZE, PROT_WRITE, parray[pa>>PAGE_SHIFT]);
+ chunksz = phdr->p_memsz - done;
+ if ( chunksz > (PAGE_SIZE - (pa & (PAGE_SIZE-1))) )
+ chunksz = PAGE_SIZE - (pa & (PAGE_SIZE-1));
+ memset(va + (pa & (PAGE_SIZE-1)), 0, chunksz);
+ munmap(va, PAGE_SIZE);
+ }
+ }
+
+ return 0;
+}
+
+
+#define ELFROUND (ELFSIZE / 8)
+
+static int
+loadelfsymtab(
+ char *elfbase, int xch, u32 dom, unsigned long *parray,
+ struct domain_setup_info *dsi)
+{
+ Elf_Ehdr *ehdr = (Elf_Ehdr *)elfbase, *sym_ehdr;
+ Elf_Shdr *shdr;
+ unsigned long maxva, symva;
+ char *p;
+ int h, i;
+
+ p = malloc(sizeof(int) + sizeof(Elf_Ehdr) +
+ ehdr->e_shnum * sizeof(Elf_Shdr));
+ if (p == NULL)
+ return 0;
+
+ maxva = (dsi->v_kernend + ELFROUND - 1) & ~(ELFROUND - 1);
+ symva = maxva;
+ maxva += sizeof(int);
+ dsi->symtab_addr = maxva;
+ dsi->symtab_len = 0;
+ maxva += sizeof(Elf_Ehdr) + ehdr->e_shnum * sizeof(Elf_Shdr);
+ maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
+
+ shdr = (Elf_Shdr *)(p + sizeof(int) + sizeof(Elf_Ehdr));
+ memcpy(shdr, elfbase + ehdr->e_shoff, ehdr->e_shnum * sizeof(Elf_Shdr));
+
+ for ( h = 0; h < ehdr->e_shnum; h++ )
+ {
+ if ( shdr[h].sh_type == SHT_STRTAB )
+ {
+ /* Look for a strtab @i linked to symtab @h. */
+ for ( i = 0; i < ehdr->e_shnum; i++ )
+ if ( (shdr[i].sh_type == SHT_SYMTAB) &&
+ (shdr[i].sh_link == h) )
+ break;
+ /* Skip symtab @h if we found no corresponding strtab @i. */
+ if ( i == ehdr->e_shnum )
+ {
+ shdr[h].sh_offset = 0;
+ continue;
+ }
+ }
+
+ if ( (shdr[h].sh_type == SHT_STRTAB) ||
+ (shdr[h].sh_type == SHT_SYMTAB) )
+ {
+ if ( parray != NULL )
+ xc_map_memcpy(maxva, elfbase + shdr[h].sh_offset, shdr[h].sh_size,
+ xch, dom, parray, dsi->v_start);
+
+ /* Mangled to be based on ELF header location. */
+ shdr[h].sh_offset = maxva - dsi->symtab_addr;
+
+ dsi->symtab_len += shdr[h].sh_size;
+ maxva += shdr[h].sh_size;
+ maxva = (maxva + ELFROUND - 1) & ~(ELFROUND - 1);
+ }
+
+ shdr[h].sh_name = 0; /* Name is NULL. */
+ }
+
+ if ( dsi->symtab_len == 0 )
+ {
+ dsi->symtab_addr = 0;
+ goto out;
+ }
+
+ if ( parray != NULL )
+ {
+ *(int *)p = maxva - dsi->symtab_addr;
+ sym_ehdr = (Elf_Ehdr *)(p + sizeof(int));
+ memcpy(sym_ehdr, ehdr, sizeof(Elf_Ehdr));
+ sym_ehdr->e_phoff = 0;
+ sym_ehdr->e_shoff = sizeof(Elf_Ehdr);
+ sym_ehdr->e_phentsize = 0;
+ sym_ehdr->e_phnum = 0;
+ sym_ehdr->e_shstrndx = SHN_UNDEF;
+
+ /* Copy total length, crafted ELF header and section header table */
+ xc_map_memcpy(symva, p, sizeof(int) + sizeof(Elf_Ehdr) +
+ ehdr->e_shnum * sizeof(Elf_Shdr), xch, dom, parray,
+ dsi->v_start);
+ }
+
+ dsi->symtab_len = maxva - dsi->symtab_addr;
+ dsi->v_end = round_pgup(maxva);
+
+ out:
+ if ( p != NULL )
+ free(p);
+
+ return 0;
+}
diff --git a/tools/libxutil/Makefile b/tools/libxutil/Makefile
index 8be24daa92..5c6c37eeef 100644
--- a/tools/libxutil/Makefile
+++ b/tools/libxutil/Makefile
@@ -1,45 +1,75 @@
+
+ifndef BUILD_PIC_LIBS
+ifeq ($(wildcard /etc/debian_version),)
+BUILD_PIC_LIBS=n
+else
+BUILD_PIC_LIBS=y
+endif
+endif
+
XEN_ROOT = ../..
-include $(XEN_ROOT)/tools/Make.defs
+INSTALL = install
+INSTALL_DATA = $(INSTALL) -m0644
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
+
+include $(XEN_ROOT)/tools/Rules.mk
CC = gcc
LIB_SRCS :=
LIB_SRCS += allocate.c
+LIB_SRCS += enum.c
LIB_SRCS += file_stream.c
LIB_SRCS += gzip_stream.c
+LIB_SRCS += hash_table.c
LIB_SRCS += iostream.c
-#LIB_SRCS += sys_net.c
+LIB_SRCS += lexis.c
+LIB_SRCS += string_stream.c
+LIB_SRCS += sxpr.c
+LIB_SRCS += sxpr_parser.c
+LIB_SRCS += sys_net.c
LIB_SRCS += sys_string.c
+LIB_SRCS += util.c
LIB_OBJS := $(LIB_SRCS:.c=.o)
+PIC_OBJS := $(LIB_SRCS:.c=.opic)
-CFLAGS += -Wall
-CFLAGS += -Werror
-CFLAGS += -g
-CFLAGS += -O3
-CFLAGS += -fno-strict-aliasing
+CFLAGS += -Wall -Werror -O3 -fno-strict-aliasing
# Get gcc to generate the dependencies for us.
CFLAGS += -Wp,-MD,.$(@F).d
DEPS = .*.d
-MAJOR := 1.3
+MAJOR := 3.0
MINOR := 0
-LIB_NAME := libxutil
-LIB := $(LIB_NAME).so
-LIB += $(LIB_NAME).so.$(MAJOR)
-LIB += $(LIB_NAME).so.$(MAJOR).$(MINOR)
+LIB := libxutil.so
+LIB += libxutil.so.$(MAJOR)
+LIB += libxutil.so.$(MAJOR).$(MINOR)
+LIB += libxutil.a
+
+all: check-for-zlib
+ $(MAKE) $(LIB)
+
+$(PIC_OBJS): %.opic: %.c
+ $(CC) $(CPPFLAGS) -DPIC $(CFLAGS) -fPIC -c -o $@ $<
-all: check-for-zlib $(LIB)
+libxutil.so: libxutil.so.$(MAJOR)
+ ln -sf $^ $@
-$(LIB_NAME).so:
- ln -sf $(LIB_NAME).so.$(MAJOR) $@
+libxutil.so.$(MAJOR): libxutil.so.$(MAJOR).$(MINOR)
+ ln -sf $^ $@
-$(LIB_NAME).so.$(MAJOR):
- ln -sf $(LIB_NAME).so.$(MAJOR).$(MINOR) $@
+ifeq ($(BUILD_PIC_LIBS),y)
+libxutil.so.$(MAJOR).$(MINOR): $(PIC_OBJS)
+ $(CC) -Wl,-soname -Wl,libxutil.so.$(MAJOR) -shared -o $@ $^
+else
+libxutil.so.$(MAJOR).$(MINOR): $(LIB_OBJS)
+ $(CC) -Wl,-soname -Wl,libxutil.so.$(MAJOR) -shared -o $@ $^
+endif
-$(LIB_NAME).so.$(MAJOR).$(MINOR): $(LIB_OBJS)
- $(CC) -Wl,-soname -Wl,$(LIB_NAME).so.$(MAJOR) -shared -o $@ $^
+libxutil.a: $(LIB_OBJS)
+ $(AR) rc $@ $^
check-for-zlib:
@if [ ! -e /usr/include/zlib.h ]; then \
@@ -50,11 +80,14 @@ check-for-zlib:
fi
install: all
- mkdir -p $(prefix)/usr/lib
- install -m0755 $(LIB) $(prefix)/usr/lib
+ [ -d $(DESTDIR)/usr/lib ] || $(INSTALL_DIR) -p $(DESTDIR)/usr/lib
+ $(INSTALL_PROG) libxutil.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/lib
+ $(INSTALL_DATA) libxutil.a $(DESTDIR)/usr/lib
+ ln -sf libxutil.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/lib/libxutil.so.$(MAJOR)
+ ln -sf libxutil.so.$(MAJOR) $(DESTDIR)/usr/lib/libxutil.so
clean:
- $(RM) *.a *.so *.o *.rpm $(LIB)
+ $(RM) *.a *.so* *.o *.opic *.rpm
$(RM) *~
$(RM) $(DEPS)
diff --git a/tools/libxutil/debug.h b/tools/libxutil/debug.h
new file mode 100644
index 0000000000..1f5a19d54d
--- /dev/null
+++ b/tools/libxutil/debug.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _XUTIL_DEBUG_H_
+#define _XUTIL_DEBUG_H_
+
+#ifndef MODULE_NAME
+#define MODULE_NAME ""
+#endif
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) printk(KERN_DEBUG "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME fmt, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME fmt, ##args)
+
+#endif
+
+#else
+
+#include <stdio.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) fprintf(stdout, "%d [DBG] " MODULE_NAME ">%s" fmt, getpid(), __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "%d [WRN] " MODULE_NAME ">%s" fmt, getpid(),__FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "%d [INF] " MODULE_NAME ">%s" fmt, getpid(),__FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "%d [ERR] " MODULE_NAME ">%s" fmt, getpid(),__FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) fprintf(stderr, "%d [WRN] " MODULE_NAME fmt, getpid(), ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "%d [INF] " MODULE_NAME fmt, getpid(), ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "%d [ERR] " MODULE_NAME fmt, getpid(), ##args)
+
+#endif
+
+#endif
+
+/** Print format for an IP address.
+ * See NIPQUAD(), HIPQUAD()
+ */
+#define IPFMT "%u.%u.%u.%u"
+
+#endif /* ! _XUTIL_DEBUG_H_ */
diff --git a/tools/xfrd/enum.c b/tools/libxutil/enum.c
index 95f6e31a87..95f6e31a87 100644
--- a/tools/xfrd/enum.c
+++ b/tools/libxutil/enum.c
diff --git a/tools/xfrd/enum.h b/tools/libxutil/enum.h
index cdc0f6f1b1..cdc0f6f1b1 100644
--- a/tools/xfrd/enum.h
+++ b/tools/libxutil/enum.h
diff --git a/tools/xfrd/hash_table.c b/tools/libxutil/hash_table.c
index 13da946e77..13da946e77 100644
--- a/tools/xfrd/hash_table.c
+++ b/tools/libxutil/hash_table.c
diff --git a/tools/xfrd/hash_table.h b/tools/libxutil/hash_table.h
index 6608b49cda..6608b49cda 100644
--- a/tools/xfrd/hash_table.h
+++ b/tools/libxutil/hash_table.h
diff --git a/tools/libxutil/iostream.c b/tools/libxutil/iostream.c
index e9980838f7..39a62173ad 100644
--- a/tools/libxutil/iostream.c
+++ b/tools/libxutil/iostream.c
@@ -1,3 +1,21 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
#include "iostream.h"
#include "sys_string.h"
diff --git a/tools/libxutil/iostream.h b/tools/libxutil/iostream.h
index 5dbe14a0b4..4786f325b2 100644
--- a/tools/libxutil/iostream.h
+++ b/tools/libxutil/iostream.h
@@ -1,14 +1,34 @@
-#ifndef _XC_LINUX_SAVE_H_
-#define _XC_LINUX_SAVE_H_
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XUTIL_IOSTREAM_H_
+#define _XUTIL_IOSTREAM_H_
#include <stdarg.h>
-#include <stdint.h>
-#include <stddef.h>
#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/types.h>
#include <linux/errno.h>
#else
#include <errno.h>
+#include <stdint.h>
+#include <stddef.h>
#endif
#include "allocate.h"
@@ -240,4 +260,4 @@ static inline int IOStream_get_written(IOStream *stream){
}
-#endif /* ! _XC_LINUX_SAVE_H_ */
+#endif /* ! _XUTIL_IOSTREAM_H_ */
diff --git a/tools/libxutil/kernel_stream.c b/tools/libxutil/kernel_stream.c
index 345b048015..a13051eec8 100644
--- a/tools/libxutil/kernel_stream.c
+++ b/tools/libxutil/kernel_stream.c
@@ -50,7 +50,7 @@ typedef struct KernelData {
char buf[BUF_N];
} KernelData;
-static int kernel_write(IOStream *s, const char *msg, int n);
+static int kernel_write(IOStream *s, const void *msg, size_t n);
static void kernel_free(IOStream *s);
static void kernel_stream_lock(IOStream *s);
static void kernel_stream_unlock(IOStream *s);
@@ -145,13 +145,13 @@ void kernel_stream_unlock(IOStream *s){
* @param args print arguments
* @return result of the print
*/
-static int kernel_write(IOStream *stream, const char *buf, int n){
+static int kernel_write(IOStream *stream, const void *buf, size_t n){
KernelData *kdata = get_kernel_data(stream);
int k;
k = kdata->buf_n - 1;
if(n < k) k = n;
memcpy(kdata->buf, buf, k);
- kdata->buf[k] = '\0'
+ kdata->buf[k] = '\0';
printk(kdata->buf);
return k;
}
@@ -167,7 +167,7 @@ static void kernel_free(IOStream *io){
KernelData *kdata;
if(io == &iokernel) return;
kdata = get_kernel_data(io);
- zero(kdata, sizeof(*kdata));
+ memset(kdata, 0, sizeof(*kdata));
deallocate(kdata);
}
#endif /* __KERNEL__ */
diff --git a/tools/xfrd/lexis.c b/tools/libxutil/lexis.c
index 26d2ec4d5b..d3441f0c38 100644
--- a/tools/xfrd/lexis.c
+++ b/tools/libxutil/lexis.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
diff --git a/tools/xfrd/lexis.h b/tools/libxutil/lexis.h
index 796a8a415a..be8fb653d3 100644
--- a/tools/xfrd/lexis.h
+++ b/tools/libxutil/lexis.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
diff --git a/tools/libxutil/socket_stream.c b/tools/libxutil/socket_stream.c
new file mode 100644
index 0000000000..9e90b4e06e
--- /dev/null
+++ b/tools/libxutil/socket_stream.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/** @file
+ * An IOStream implementation using sockets.
+ */
+#ifndef __KERNEL__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include "allocate.h"
+#include "socket_stream.h"
+
+#define MODULE_NAME "sock"
+#define DEBUG 0
+//#undef DEBUG
+#include "debug.h"
+
+static int socket_read(IOStream *s, void *buf, size_t n);
+static int socket_write(IOStream *s, const void *buf, size_t n);
+static int socket_error(IOStream *s);
+static int socket_close(IOStream *s);
+static void socket_free(IOStream *s);
+static int socket_flush(IOStream *s);
+
+/** Methods used by a socket IOStream. */
+static const IOMethods socket_methods = {
+ read: socket_read,
+ write: socket_write,
+ error: socket_error,
+ close: socket_close,
+ free: socket_free,
+ flush: socket_flush,
+};
+
+/** Get the socket data.
+ *
+ * @param io socket stream
+ * @return data
+ */
+static inline SocketData * socket_data(IOStream *io){
+ return (SocketData *)io->data;
+}
+
+/** Test if a stream is a socket stream.
+ *
+ * @param io stream
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_check(IOStream *io){
+ return (io && io->methods == &socket_methods ? 0 : -EINVAL);
+}
+
+/** Get the data for a socket stream.
+ *
+ * @param io stream
+ * @param data return value for the data
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_data(IOStream *io, SocketData **data){
+ int err = socket_stream_check(io);
+ if(err){
+ *data = NULL;
+ } else {
+ *data = socket_data(io);
+ }
+ return err;
+}
+
+/** Set the destination address for a socket stream.
+ *
+ * @param io stream
+ * @param addr address
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr){
+ int err = 0;
+ SocketData *data = NULL;
+ err = socket_stream_data(io, &data);
+ if(!err){
+ data->daddr = *addr;
+ }
+ return err;
+}
+
+/** Set the send flags for a socket stream.
+ *
+ * @param io stream
+ * @param flags flags
+ * @return 0 if a socket stream, -EINVAL if not
+ */
+int socket_stream_set_flags(IOStream *io, int flags){
+ int err = 0;
+ SocketData *data = NULL;
+ err = socket_stream_data(io, &data);
+ if(!err){
+ data->flags = flags;
+ }
+ return err;
+}
+
+/** Write to the underlying socket using sendto.
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to write
+ * @return number of bytes written
+ */
+static int socket_write(IOStream *s, const void *buf, size_t n){
+ SocketData *data = socket_data(s);
+ struct sockaddr *daddr = (struct sockaddr *)&data->daddr;
+ socklen_t daddr_n = sizeof(data->daddr);
+ int k;
+ dprintf("> sock=%d addr=%s:%d n=%d\n",
+ data->fd, inet_ntoa(data->daddr.sin_addr), ntohs(data->daddr.sin_port), n);
+ if(0){
+ struct sockaddr_in self = {};
+ socklen_t self_n;
+ getsockname(data->fd, (struct sockaddr *)&self, &self_n);
+ dprintf("> sockname sock=%d %s:%d\n",
+ data->fd, inet_ntoa(self.sin_addr), ntohs(self.sin_port));
+ }
+ k = sendto(data->fd, buf, n, data->flags, daddr, daddr_n);
+ dprintf("> sendto=%d\n", k);
+ return k;
+}
+
+/** Read from the underlying stream using recv();
+ *
+ * @param stream input
+ * @param buf where to put input
+ * @param n number of bytes to read
+ * @return number of bytes read
+ */
+static int socket_read(IOStream *s, void *buf, size_t n){
+ SocketData *data = socket_data(s);
+ int k;
+ struct sockaddr *saddr = (struct sockaddr *)&data->saddr;
+ socklen_t saddr_n = sizeof(data->saddr);
+ k = recvfrom(data->fd, buf, n, data->flags, saddr, &saddr_n);
+ return k;
+}
+
+/** Flush the socket (no-op).
+ *
+ * @param s socket stream
+ * @return 0 on success, error code otherwise
+ */
+static int socket_flush(IOStream *s){
+ return 0;
+}
+
+/** Check if a socket stream has an error (no-op).
+ *
+ * @param s socket stream
+ * @return 1 if has an error, 0 otherwise
+ */
+static int socket_error(IOStream *s){
+ // Read SOL_SOCKET/SO_ERROR ?
+ return 0;
+}
+
+/** Close a socket stream.
+ *
+ * @param s socket stream to close
+ * @return result of the close
+ */
+static int socket_close(IOStream *s){
+ SocketData *data = socket_data(s);
+ return close(data->fd);
+}
+
+/** Free a socket stream.
+ *
+ * @param s socket stream
+ */
+static void socket_free(IOStream *s){
+ SocketData *data = socket_data(s);
+ deallocate(data);
+}
+
+/** Create an IOStream for a socket.
+ *
+ * @param fd socket to wtap
+ * @return new IOStream using fd for i/o
+ */
+IOStream *socket_stream_new(int fd){
+ int err = -ENOMEM;
+ IOStream *io = NULL;
+ SocketData *data = NULL;
+
+ io = ALLOCATE(IOStream);
+ if(!io) goto exit;
+ io->methods = &socket_methods;
+ data = ALLOCATE(SocketData);
+ if(!data) goto exit;
+ io->data = data;
+ data->fd = fd;
+ data->buf_n = sizeof(data->buf);
+ err = 0;
+ exit:
+ if(err){
+ if(io){
+ if(data) deallocate(data);
+ deallocate(io);
+ io = NULL;
+ }
+ }
+ return io;
+}
+
+#endif
diff --git a/tools/libxutil/socket_stream.h b/tools/libxutil/socket_stream.h
new file mode 100644
index 0000000000..9da23e9f01
--- /dev/null
+++ b/tools/libxutil/socket_stream.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XEN_LIB_SOCKET_STREAM_H_
+#define _XEN_LIB_SOCKET_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+#include <stdio.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/** Data associated with a socket stream. */
+typedef struct SocketData {
+ /** The socket file descriptor. */
+ int fd;
+ /** Source address from last read (recvfrom). */
+ struct sockaddr_in saddr;
+ /** Destination address for writes (sendto). */
+ struct sockaddr_in daddr;
+ /** Write flags (sendto). */
+ int flags;
+ /** Buffer size. */
+ int buf_n;
+ /** Buffer for formatted printing. */
+ char buf[1024];
+} SocketData;
+
+extern IOStream *socket_stream_new(int fd);
+extern int socket_stream_data(IOStream *io, SocketData **data);
+extern int socket_stream_check(IOStream *io);
+extern int socket_stream_set_addr(IOStream *io, struct sockaddr_in *addr);
+extern int socket_stream_set_flags(IOStream *io, int flags);
+
+#endif
+#endif /* !_XEN_LIB_SOCKET_STREAM_H_ */
diff --git a/tools/libxutil/string_stream.c b/tools/libxutil/string_stream.c
index 495238d74a..a535c922ff 100644
--- a/tools/libxutil/string_stream.c
+++ b/tools/libxutil/string_stream.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2001, 2002 Hewlett-Packard Company.
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
diff --git a/tools/libxutil/string_stream.h b/tools/libxutil/string_stream.h
index b6cbc0f3df..246e63d2fe 100644
--- a/tools/libxutil/string_stream.h
+++ b/tools/libxutil/string_stream.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2001, 2002 Hewlett-Packard Company.
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
diff --git a/tools/xfrd/sxpr.c b/tools/libxutil/sxpr.c
index 81fd14801c..5a9364a317 100644
--- a/tools/xfrd/sxpr.c
+++ b/tools/libxutil/sxpr.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -21,7 +22,12 @@
#include "hash_table.h"
#include "sxpr.h"
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#else
#include <errno.h>
+#endif
+
#undef free
/** @file
diff --git a/tools/xfrd/sxpr.h b/tools/libxutil/sxpr.h
index a5875c76bd..8b01e84736 100644
--- a/tools/xfrd/sxpr.h
+++ b/tools/libxutil/sxpr.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
@@ -16,7 +17,12 @@
#ifndef _XUTIL_SXPR_H_
#define _XUTIL_SXPR_H_
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/types.h>
+#else
#include <stdint.h>
+#endif
#include "hash_table.h"
#include "iostream.h"
diff --git a/tools/xfrd/sxpr_parser.c b/tools/libxutil/sxpr_parser.c
index dbe386a862..472088936d 100644
--- a/tools/xfrd/sxpr_parser.c
+++ b/tools/libxutil/sxpr_parser.c
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
#ifdef __KERNEL__
# include <linux/config.h>
@@ -242,6 +258,9 @@ int Parser_pop(Parser *p){
int err = 0;
ParserState *s = p->state;
p->state = s->parent;
+ if (p->start_state == s) {
+ p->start_state = NULL;
+ }
ParserState_free(s);
return err;
}
@@ -374,7 +393,7 @@ Sxpr Parser_get_all(Parser *p){
if(CONSP(p->val)){
v = p->val;
p->val = ONONE;
- } else if(CONSP(p->start_state->val)){
+ } else if(p->start_state && CONSP(p->start_state->val)){
v = p->start_state->val;
p->start_state->val = ONULL;
v = nrev(v);
@@ -808,26 +827,13 @@ int at_eof(Parser *p){
return p->eof;
}
-//#define SXPR_PARSER_MAIN
#ifdef SXPR_PARSER_MAIN
/* Stuff for standalone testing. */
#include "file_stream.h"
#include "string_stream.h"
-int stringof(Sxpr exp, char **s){
- int err = 0;
- if(ATOMP(exp)){
- *s = atom_name(exp);
- } else if(STRINGP(exp)){
- *s = string_string(exp);
- } else {
- err = -EINVAL;
- *s = NULL;
- }
- return err;
-}
-
+extern int stringof(Sxpr exp, char **s);
int child_string(Sxpr exp, Sxpr key, char **s){
int err = 0;
Sxpr val = sxpr_child_value(exp, key, ONONE);
@@ -835,22 +841,7 @@ int child_string(Sxpr exp, Sxpr key, char **s){
return err;
}
-int intof(Sxpr exp, int *v){
- int err = 0;
- char *s;
- unsigned long l;
- if(INTP(exp)){
- *v = OBJ_INT(exp);
- } else {
- err = stringof(exp, &s);
- if(err) goto exit;
- err = convert_atoul(s, &l);
- *v = (int)l;
- }
- exit:
- return err;
-}
-
+extern int intof(Sxpr exp, int *v);
int child_int(Sxpr exp, Sxpr key, int *v){
int err = 0;
Sxpr val = sxpr_child_value(exp, key, ONONE);
diff --git a/tools/xfrd/sxpr_parser.h b/tools/libxutil/sxpr_parser.h
index 64a417608c..0736fd3d40 100644
--- a/tools/xfrd/sxpr_parser.h
+++ b/tools/libxutil/sxpr_parser.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
diff --git a/tools/libxutil/sys_net.c b/tools/libxutil/sys_net.c
index f38d919d9c..94da58d63e 100644
--- a/tools/libxutil/sys_net.c
+++ b/tools/libxutil/sys_net.c
@@ -232,21 +232,17 @@ char *get_port_service(unsigned long port){
int convert_service_to_port(const char *s, unsigned long *port){
int err = 0;
unsigned long value;
- printf("%s> %s\n", __FUNCTION__, s);
if(convert_atoul(s, &value) == 0){
int ok = (0 <= value) && (value <= PORT_MAX);
- printf("> value = %ld\n", value);
if(ok){
value = htons((unsigned short)value);
} else {
err = -EINVAL;
}
} else {
- printf("> get_service_port...\n");
err = get_service_port(s, &value);
}
*port = (err ? 0: value);
- printf("%s< err=%d\n", __FUNCTION__, err);
return err;
}
diff --git a/tools/libxutil/util.c b/tools/libxutil/util.c
new file mode 100644
index 0000000000..0ac388b3b8
--- /dev/null
+++ b/tools/libxutil/util.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2002 - 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "sys_net.h"
+#include "sys_string.h"
+
+#ifndef __KERNEL__
+# include <grp.h>
+# include <pwd.h>
+#endif
+
+#include "util.h"
+
+
+/** @file Various utility functions.
+ */
+
+/** Print an address (in network order) as an IPv4 address string
+ * in dot notation.
+ *
+ * @param io where to print address
+ * @param address to print (in network order)
+ * @return bytes printed
+ */
+int print_address(IOStream *io, unsigned long address){
+#ifdef __KERNEL__
+ address = ntohl(address);
+ return IOStream_print(io, "%u.%u.%u.%u",
+ (unsigned)((address >> 24) & 0xff),
+ (unsigned)((address >> 16) & 0xff),
+ (unsigned)((address >> 8) & 0xff),
+ (unsigned)((address ) & 0xff));
+#else
+ struct in_addr inaddr = { s_addr: address };
+ return IOStream_print(io, inet_ntoa(inaddr));
+#endif
+}
+
+/** Get the protocol number for a protocol.
+ *
+ * @param name protocol name
+ * @param protocol where to put the protocol number
+ * @return 0 if OK, error otherwise
+ */
+int get_protocol_number(char *name, unsigned long *protocol){
+#ifdef __KERNEL__
+ return -1;
+#else
+ struct protoent *proto = getprotobyname(name);
+ if(!proto){
+ return -1;
+ }
+ *protocol = proto->p_proto;
+ return 0;
+#endif
+}
+
+/** Get the protocol name for a protocol number.
+ *
+ * @param protocol number
+ * @return name or null
+ */
+char *get_protocol_name(unsigned long protocol){
+#ifdef __KERNEL__
+ return 0;
+#else
+ struct protoent *proto = getprotobynumber(protocol);
+ if(!proto){
+ return 0;
+ }
+ return proto->p_name;
+#endif
+}
+
+/** Get the host name for an address.
+ *
+ * @param addr address
+ * @return host name or null
+ */
+char *get_host_name(unsigned long addr){
+#ifdef __KERNEL__
+ return 0;
+#else
+ struct in_addr inaddr;
+ struct hostent *host = 0;
+
+ inaddr.s_addr = addr;
+ host = gethostbyaddr((char*)&inaddr, sizeof(inaddr), AF_INET);
+ if(!host) return NULL;
+ return host->h_name;
+#endif
+}
diff --git a/tools/libxutil/util.h b/tools/libxutil/util.h
new file mode 100644
index 0000000000..b4a170512f
--- /dev/null
+++ b/tools/libxutil/util.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2002 - 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _XEN_LIB_UTIL_H_
+#define _XEN_LIB_UTIL_H_
+
+#include "iostream.h"
+
+extern int print_address(IOStream *io, unsigned long address);
+extern int get_protocol_number(char *name, unsigned long *protocol);
+extern char *get_protocol_name(unsigned long protocol);
+extern char *get_host_name(unsigned long addr);
+
+#endif /* ! _XEN_LIB_UTIL_H_ */
diff --git a/tools/misc/Makefile b/tools/misc/Makefile
index e3eaad9b2c..39bfc424e6 100644
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -1,40 +1,41 @@
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
XEN_ROOT=../..
-include $(XEN_ROOT)/tools/Make.defs
+include $(XEN_ROOT)/tools/Rules.mk
-CC = gcc
-CFLAGS = -Wall -O3
+CC = gcc
+CFLAGS += -Wall -Werror -O3
-INCLUDES += -I $(XEN_HYPERVISOR_IFS)
-INCLUDES += -I $(XEN_LINUX_INCLUDE)
INCLUDES += -I $(XEN_XC)
INCLUDES += -I $(XEN_LIBXC)
-INCLUDES += -I $(XEN_LIBXUTIL)
-
-CFLAGS += $(INCLUDES)
+CFLAGS += $(INCLUDES)
HDRS = $(wildcard *.h)
-SRCS = $(wildcard *.c)
-OBJS = $(patsubst %.c,%.o,$(SRCS))
-TARGETS = xen_cpuperf
+TARGETS = xenperf
INSTALL_BIN = $(TARGETS) xencons
-INSTALL_SBIN = netfix xm xend xensv
+INSTALL_SBIN = netfix xm xend xensv xenperf
all: $(TARGETS)
$(MAKE) -C miniterm
install: all
- mkdir -p $(prefix)/usr/bin
- mkdir -p $(prefix)/usr/sbin
- install -m0755 $(INSTALL_BIN) $(prefix)/usr/bin
- install -m0755 $(INSTALL_SBIN) $(prefix)/usr/sbin
- $(MAKE) -C miniterm install
+ [ -d $(DESTDIR)/usr/bin ] || $(INSTALL_DIR) $(DESTDIR)/usr/bin
+ [ -d $(DESTDIR)/usr/sbin ] || $(INSTALL_DIR) $(DESTDIR)/usr/sbin
+ $(INSTALL_PROG) $(INSTALL_BIN) $(DESTDIR)/usr/bin
+ $(INSTALL_PROG) $(INSTALL_SBIN) $(DESTDIR)/usr/sbin
+# No sense in installing miniterm on the Xen box.
+# $(MAKE) -C miniterm install
clean:
$(RM) *.o $(TARGETS) *~
- $(MAKE) -C miniterm clean
+ $(MAKE) -C miniterm clean
+
+%.o: %.c $(HDRS) Makefile
+ $(CC) -c $(CFLAGS) -o $@ $<
-%: %.c $(HDRS) Makefile
- $(CC) $(CFLAGS) -o $@ $<
+$(TARGETS): %: %.o Makefile
+ $(CC) $(CFLAGS) -o $@ $< -L$(XEN_LIBXC) -lxc -L$(XEN_LIBXUTIL) -lxutil
diff --git a/tools/misc/miniterm/Makefile b/tools/misc/miniterm/Makefile
index 86e1bf0ccf..4051eb61de 100644
--- a/tools/misc/miniterm/Makefile
+++ b/tools/misc/miniterm/Makefile
@@ -1,3 +1,6 @@
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
CC = gcc
CFLAGS = -Wall -O3
@@ -6,8 +9,8 @@ TARGET = miniterm
all: $(TARGET)
install: all
- mkdir -p $(prefix)/usr/bin
- install -m0755 $(TARGET) $(prefix)/usr/bin
+ [ -d $(DESTDIR)/usr/bin ] || $(INSTALL_DIR) $(DESTDIR)/usr/bin
+ $(INSTALL_PROG) $(TARGET) $(DESTDIR)/usr/bin
clean:
$(RM) *.o $(TARGET) *~
diff --git a/tools/misc/netfix b/tools/misc/netfix
index 429f579c02..ab5a04cdef 100644
--- a/tools/misc/netfix
+++ b/tools/misc/netfix
@@ -8,6 +8,9 @@
#============================================================================
from getopt import getopt
+
+# Default install path for Xen binary packages.
+sys.path = [ '/usr/lib/python' ] + sys.path
from xen.util.Brctl import *
short_options = 'hvqni:b:c'
diff --git a/tools/misc/p4perf.h b/tools/misc/p4perf.h
deleted file mode 100644
index 4f681b636d..0000000000
--- a/tools/misc/p4perf.h
+++ /dev/null
@@ -1,559 +0,0 @@
-/*
- * For P6 use PERFCTR1 (0 used for APIC NMI watchdog). Must setup after
- * APIC NMI watchdog setup. Note that if this previous setup doesn't happen
- * we still must enable both counters.
- *
- * P4 Xeon with Hyperthreading has counters per physical package which can
- * count events from either logical CPU. However, in many cases more than
- * ECSR and CCCR/counter can be used to count the same event. For instr or
- * uops retired, use either ESCR0/IQ_CCCR0 ESCR1/IQ_CCCR2.
- *
- * USE CONFIG_MPENTIUM4_HT for a P4 Xeon with hyperthreading.
- *
- * Note that the counters may be initialised on each logical processor
- * which will cause each physical processor to be initialised twice. This
- * should not cause a problem.
- */
-
-#ifndef P4PERF_H
-#define P4PERF_H
-
-#ifdef __KERNEL__
-#include <asm/msr.h>
-#endif
-
-/*****************************************************************************
- * Performance counter configuration. *
- *****************************************************************************/
-
-#ifndef P6_EVNTSEL_OS
-# define P6_EVNTSEL_OS (1 << 17)
-# define P6_EVNTSEL_USR (1 << 16)
-# define P6_EVNTSEL_E (1 << 18)
-# define P6_EVNTSEL_EN (1 << 22)
-#endif
-#define P6_PERF_INST_RETIRED 0xc0
-#define P6_PERF_UOPS_RETIRED 0xc2
-
-#define P4_ESCR_USR (1 << 2)
-#define P4_ESCR_OS (1 << 3)
-#define P4_ESCR_T0_USR (1 << 2) /* First logical CPU */
-#define P4_ESCR_T0_OS (1 << 3)
-#define P4_ESCR_T1_USR (1 << 0) /* Second logical CPU */
-#define P4_ESCR_T1_OS (1 << 1)
-#define P4_ESCR_TE (1 << 4)
-#define P4_ESCR_THREADS(t) (t)
-#define P4_ESCR_TV(tag) (tag << 5)
-#define P4_ESCR_EVNTSEL(e) (e << 25)
-#define P4_ESCR_EVNTMASK(e) (e << 9)
-
-#define P4_ESCR_EVNTSEL_FRONT_END 0x08
-#define P4_ESCR_EVNTSEL_EXECUTION 0x0c
-#define P4_ESCR_EVNTSEL_REPLAY 0x09
-#define P4_ESCR_EVNTSEL_INSTR_RETIRED 0x02
-#define P4_ESCR_EVNTSEL_UOPS_RETIRED 0x01
-#define P4_ESCR_EVNTSEL_UOP_TYPE 0x02
-#define P4_ESCR_EVNTSEL_RET_MBR_TYPE 0x05
-//#define P4_ESCR_EVNTSEL_RET_MBR_TYPE 0x04
-
-#define P4_ESCR_EVNTMASK_FE_NBOGUS 0x01
-#define P4_ESCR_EVNTMASK_FE_BOGUS 0x02
-
-#define P4_ESCR_EVNTMASK_EXEC_NBOGUS0 0x01
-#define P4_ESCR_EVNTMASK_EXEC_NBOGUS1 0x02
-#define P4_ESCR_EVNTMASK_EXEC_NBOGUS2 0x04
-#define P4_ESCR_EVNTMASK_EXEC_NBOGUS3 0x08
-#define P4_ESCR_EVNTMASK_EXEC_BOGUS0 0x10
-#define P4_ESCR_EVNTMASK_EXEC_BOGUS1 0x20
-#define P4_ESCR_EVNTMASK_EXEC_BOGUS2 0x40
-#define P4_ESCR_EVNTMASK_EXEC_BOGUS3 0x80
-
-#define P4_ESCR_EVNTMASK_REPLAY_NBOGUS 0x01
-#define P4_ESCR_EVNTMASK_REPLAY_BOGUS 0x02
-
-#define P4_ESCR_EVNTMASK_IRET_NB_NTAG 0x01
-#define P4_ESCR_EVNTMASK_IRET_NB_TAG 0x02
-#define P4_ESCR_EVNTMASK_IRET_B_NTAG 0x04
-#define P4_ESCR_EVNTMASK_IRET_B_TAG 0x08
-
-#define P4_ESCR_EVNTMASK_URET_NBOGUS 0x01
-#define P4_ESCR_EVNTMASK_URET_BOGUS 0x02
-
-#define P4_ESCR_EVNTMASK_UOP_LOADS 0x02
-#define P4_ESCR_EVNTMASK_UOP_STORES 0x04
-
-#define P4_ESCR_EVNTMASK_RMBRT_COND 0x02
-#define P4_ESCR_EVNTMASK_RMBRT_CALL 0x04
-#define P4_ESCR_EVNTMASK_RMBRT_RETURN 0x08
-#define P4_ESCR_EVNTMASK_RMBRT_INDIR 0x10
-
-#define P4_ESCR_EVNTMASK_RBRT_COND 0x02
-#define P4_ESCR_EVNTMASK_RBRT_CALL 0x04
-#define P4_ESCR_EVNTMASK_RBRT_RETURN 0x08
-#define P4_ESCR_EVNTMASK_RBRT_INDIR 0x10
-
-//#define P4_ESCR_EVNTMASK_INSTR_RETIRED 0x01 /* Non bogus, not tagged */
-//#define P4_ESCR_EVNTMASK_UOPS_RETIRED 0x01 /* Non bogus */
-
-#define P4_CCCR_OVF (1 << 31)
-#define P4_CCCR_CASCADE (1 << 30)
-#define P4_CCCR_FORCE_OVF (1 << 25)
-#define P4_CCCR_EDGE (1 << 24)
-#define P4_CCCR_COMPLEMENT (1 << 19)
-#define P4_CCCR_COMPARE (1 << 18)
-#define P4_CCCR_THRESHOLD(t) (t << 20)
-#define P4_CCCR_ENABLE (1 << 12)
-#define P4_CCCR_ESCR(escr) (escr << 13)
-#define P4_CCCR_ACTIVE_THREAD(t) (t << 16) /* Set to 11 */
-#define P4_CCCR_OVF_PMI_T0 (1 << 26)
-#define P4_CCCR_OVF_PMI_T1 (1 << 27)
-#define P4_CCCR_RESERVED (3 << 16)
-#define P4_CCCR_OVF_PMI (1 << 26)
-
-// BPU
-#define MSR_P4_BPU_COUNTER0 0x300
-#define MSR_P4_BPU_COUNTER1 0x301
-#define MSR_P4_BPU_CCCR0 0x360
-#define MSR_P4_BPU_CCCR1 0x361
-
-#define MSR_P4_BPU_COUNTER2 0x302
-#define MSR_P4_BPU_COUNTER3 0x303
-#define MSR_P4_BPU_CCCR2 0x362
-#define MSR_P4_BPU_CCCR3 0x363
-
-#define MSR_P4_BSU_ESCR0 0x3a0
-#define MSR_P4_FSB_ESCR0 0x3a2
-#define MSR_P4_MOB_ESCR0 0x3aa
-#define MSR_P4_PMH_ESCR0 0x3ac
-#define MSR_P4_BPU_ESCR0 0x3b2
-#define MSR_P4_IS_ESCR0 0x3b4
-#define MSR_P4_ITLB_ESCR0 0x3b6
-#define MSR_P4_IX_ESCR0 0x3c8
-
-#define P4_BSU_ESCR0_NUMBER 7
-#define P4_FSB_ESCR0_NUMBER 6
-#define P4_MOB_ESCR0_NUMBER 2
-#define P4_PMH_ESCR0_NUMBER 4
-#define P4_BPU_ESCR0_NUMBER 0
-#define P4_IS_ESCR0_NUMBER 1
-#define P4_ITLB_ESCR0_NUMBER 3
-#define P4_IX_ESCR0_NUMBER 5
-
-#define MSR_P4_BSU_ESCR1 0x3a1
-#define MSR_P4_FSB_ESCR1 0x3a3
-#define MSR_P4_MOB_ESCR1 0x3ab
-#define MSR_P4_PMH_ESCR1 0x3ad
-#define MSR_P4_BPU_ESCR1 0x3b3
-#define MSR_P4_IS_ESCR1 0x3b5
-#define MSR_P4_ITLB_ESCR1 0x3b7
-#define MSR_P4_IX_ESCR1 0x3c9
-
-#define P4_BSU_ESCR1_NUMBER 7
-#define P4_FSB_ESCR1_NUMBER 6
-#define P4_MOB_ESCR1_NUMBER 2
-#define P4_PMH_ESCR1_NUMBER 4
-#define P4_BPU_ESCR1_NUMBER 0
-#define P4_IS_ESCR1_NUMBER 1
-#define P4_ITLB_ESCR1_NUMBER 3
-#define P4_IX_ESCR1_NUMBER 5
-
-// MS
-#define MSR_P4_MS_COUNTER0 0x304
-#define MSR_P4_MS_COUNTER1 0x305
-#define MSR_P4_MS_CCCR0 0x364
-#define MSR_P4_MS_CCCR1 0x365
-
-#define MSR_P4_MS_COUNTER2 0x306
-#define MSR_P4_MS_COUNTER3 0x307
-#define MSR_P4_MS_CCCR2 0x366
-#define MSR_P4_MS_CCCR3 0x367
-
-#define MSR_P4_MS_ESCR0 0x3c0
-#define MSR_P4_TBPU_ESCR0 0x3c2
-#define MSR_P4_TC_ESCR0 0x3c4
-
-#define P4_MS_ESCR0_NUMBER 0
-#define P4_TBPU_ESCR0_NUMBER 2
-#define P4_TC_ESCR0_NUMBER 1
-
-#define MSR_P4_MS_ESCR1 0x3c1
-#define MSR_P4_TBPU_ESCR1 0x3c3
-#define MSR_P4_TC_ESCR1 0x3c5
-
-#define P4_MS_ESCR1_NUMBER 0
-#define P4_TBPU_ESCR1_NUMBER 2
-#define P4_TC_ESCR1_NUMBER 1
-
-// FLAME
-#define MSR_P4_FLAME_COUNTER0 0x308
-#define MSR_P4_FLAME_COUNTER1 0x309
-#define MSR_P4_FLAME_CCCR0 0x368
-#define MSR_P4_FLAME_CCCR1 0x369
-
-#define MSR_P4_FLAME_COUNTER2 0x30a
-#define MSR_P4_FLAME_COUNTER3 0x30b
-#define MSR_P4_FLAME_CCCR2 0x36a
-#define MSR_P4_FLAME_CCCR3 0x36b
-
-#define MSR_P4_FIRM_ESCR0 0x3a4
-#define MSR_P4_FLAME_ESCR0 0x3a6
-#define MSR_P4_DAC_ESCR0 0x3a8
-#define MSR_P4_SAAT_ESCR0 0x3ae
-#define MSR_P4_U2L_ESCR0 0x3b0
-
-#define P4_FIRM_ESCR0_NUMBER 1
-#define P4_FLAME_ESCR0_NUMBER 0
-#define P4_DAC_ESCR0_NUMBER 5
-#define P4_SAAT_ESCR0_NUMBER 2
-#define P4_U2L_ESCR0_NUMBER 3
-
-#define MSR_P4_FIRM_ESCR1 0x3a5
-#define MSR_P4_FLAME_ESCR1 0x3a7
-#define MSR_P4_DAC_ESCR1 0x3a9
-#define MSR_P4_SAAT_ESCR1 0x3af
-#define MSR_P4_U2L_ESCR1 0x3b1
-
-#define P4_FIRM_ESCR1_NUMBER 1
-#define P4_FLAME_ESCR1_NUMBER 0
-#define P4_DAC_ESCR1_NUMBER 5
-#define P4_SAAT_ESCR1_NUMBER 2
-#define P4_U2L_ESCR1_NUMBER 3
-
-// IQ
-#define MSR_P4_IQ_COUNTER0 0x30c
-#define MSR_P4_IQ_COUNTER1 0x30d
-#define MSR_P4_IQ_CCCR0 0x36c
-#define MSR_P4_IQ_CCCR1 0x36d
-
-#define MSR_P4_IQ_COUNTER2 0x30e
-#define MSR_P4_IQ_COUNTER3 0x30f
-#define MSR_P4_IQ_CCCR2 0x36e
-#define MSR_P4_IQ_CCCR3 0x36f
-
-#define MSR_P4_IQ_COUNTER4 0x310
-#define MSR_P4_IQ_COUNTER5 0x311
-#define MSR_P4_IQ_CCCR4 0x370
-#define MSR_P4_IQ_CCCR5 0x371
-
-#define MSR_P4_CRU_ESCR0 0x3b8
-#define MSR_P4_CRU_ESCR2 0x3cc
-#define MSR_P4_CRU_ESCR4 0x3e0
-#define MSR_P4_IQ_ESCR0 0x3ba
-#define MSR_P4_RAT_ESCR0 0x3bc
-#define MSR_P4_SSU_ESCR0 0x3be
-#define MSR_P4_ALF_ESCR0 0x3ca
-
-#define P4_CRU_ESCR0_NUMBER 4
-#define P4_CRU_ESCR2_NUMBER 5
-#define P4_CRU_ESCR4_NUMBER 6
-#define P4_IQ_ESCR0_NUMBER 0
-#define P4_RAT_ESCR0_NUMBER 2
-#define P4_SSU_ESCR0_NUMBER 3
-#define P4_ALF_ESCR0_NUMBER 1
-
-#define MSR_P4_CRU_ESCR1 0x3b9
-#define MSR_P4_CRU_ESCR3 0x3cd
-#define MSR_P4_CRU_ESCR5 0x3e1
-#define MSR_P4_IQ_ESCR1 0x3bb
-#define MSR_P4_RAT_ESCR1 0x3bd
-#define MSR_P4_ALF_ESCR1 0x3cb
-
-#define P4_CRU_ESCR1_NUMBER 4
-#define P4_CRU_ESCR3_NUMBER 5
-#define P4_CRU_ESCR5_NUMBER 6
-#define P4_IQ_ESCR1_NUMBER 0
-#define P4_RAT_ESCR1_NUMBER 2
-#define P4_ALF_ESCR1_NUMBER 1
-
-#define P4_BPU_COUNTER0_NUMBER 0
-#define P4_BPU_COUNTER1_NUMBER 1
-#define P4_BPU_COUNTER2_NUMBER 2
-#define P4_BPU_COUNTER3_NUMBER 3
-
-#define P4_MS_COUNTER0_NUMBER 4
-#define P4_MS_COUNTER1_NUMBER 5
-#define P4_MS_COUNTER2_NUMBER 6
-#define P4_MS_COUNTER3_NUMBER 7
-
-#define P4_FLAME_COUNTER0_NUMBER 8
-#define P4_FLAME_COUNTER1_NUMBER 9
-#define P4_FLAME_COUNTER2_NUMBER 10
-#define P4_FLAME_COUNTER3_NUMBER 11
-
-#define P4_IQ_COUNTER0_NUMBER 12
-#define P4_IQ_COUNTER1_NUMBER 13
-#define P4_IQ_COUNTER2_NUMBER 14
-#define P4_IQ_COUNTER3_NUMBER 15
-#define P4_IQ_COUNTER4_NUMBER 16
-#define P4_IQ_COUNTER5_NUMBER 17
-
-/* PEBS
- */
-#define MSR_P4_PEBS_ENABLE 0x3F1
-#define MSR_P4_PEBS_MATRIX_VERT 0x3F2
-
-#define P4_PEBS_ENABLE_MY_THR (1 << 25)
-#define P4_PEBS_ENABLE_OTH_THR (1 << 26)
-#define P4_PEBS_ENABLE (1 << 24)
-#define P4_PEBS_BIT0 (1 << 0)
-#define P4_PEBS_BIT1 (1 << 1)
-#define P4_PEBS_BIT2 (1 << 2)
-
-#define P4_PEBS_MATRIX_VERT_BIT0 (1 << 0)
-#define P4_PEBS_MATRIX_VERT_BIT1 (1 << 1)
-#define P4_PEBS_MATRIX_VERT_BIT2 (1 << 2)
-
-/* Replay tagging.
- */
-#define P4_REPLAY_TAGGING_PEBS_L1LMR P4_PEBS_BIT0
-#define P4_REPLAY_TAGGING_PEBS_L2LMR P4_PEBS_BIT1
-#define P4_REPLAY_TAGGING_PEBS_DTLMR P4_PEBS_BIT2
-#define P4_REPLAY_TAGGING_PEBS_DTSMR P4_PEBS_BIT2
-#define P4_REPLAY_TAGGING_PEBS_DTAMR P4_PEBS_BIT2
-
-#define P4_REPLAY_TAGGING_VERT_L1LMR P4_PEBS_MATRIX_VERT_BIT0
-#define P4_REPLAY_TAGGING_VERT_L2LMR P4_PEBS_MATRIX_VERT_BIT0
-#define P4_REPLAY_TAGGING_VERT_DTLMR P4_PEBS_MATRIX_VERT_BIT0
-#define P4_REPLAY_TAGGING_VERT_DTSMR P4_PEBS_MATRIX_VERT_BIT1
-#define P4_REPLAY_TAGGING_VERT_DTAMR P4_PEBS_MATRIX_VERT_BIT0 | P4_PEBS_MATRIX_VERT_BIT1
-
-
-
-
-/*****************************************************************************
- * *
- *****************************************************************************/
-
-// x87_FP_uop
-#define EVENT_SEL_x87_FP_uop 0x04
-#define EVENT_MASK_x87_FP_uop_ALL (1 << 15)
-
-// execution event (at retirement)
-#define EVENT_SEL_execution_event 0x0C
-
-// scalar_SP_uop
-#define EVENT_SEL_scalar_SP_uop 0x0a
-#define EVENT_MASK_scalar_SP_uop_ALL (1 << 15)
-
-// scalar_DP_uop
-#define EVENT_SEL_scalar_DP_uop 0x0e
-#define EVENT_MASK_scalar_DP_uop_ALL (1 << 15)
-
-// Instruction retired
-#define EVENT_SEL_instr_retired 0x02
-#define EVENT_MASK_instr_retired_ALL 0x0f
-
-// uOps retired
-#define EVENT_SEL_uops_retired 0x01
-#define EVENT_MASK_uops_retired_ALL 0x03
-
-// L1 misses retired
-#define EVENT_SEL_replay_event 0x09
-#define EVENT_MASK_replay_event_ALL 0x03
-
-// Trace cache
-#define EVENT_SEL_BPU_fetch_request 0x03
-#define EVENT_MASK_BPU_fetch_request_TCMISS 0x01
-
-// Bus activity
-#define EVENT_SEL_FSB_data_activity 0x17
-#define EVENT_MASK_FSB_data_activity_DRDY_DRV 0x01
-#define EVENT_MASK_FSB_data_activity_DRDY_OWN 0x02
-#define EVENT_MASK_FSB_data_activity_DRDY_OOTHER 0x04
-#define EVENT_MASK_FSB_data_activity_DBSY_DRV 0x08
-#define EVENT_MASK_FSB_data_activity_DBSY_OWN 0x10
-#define EVENT_MASK_FSB_data_activity_DBSY_OOTHER 0x20
-
-// Cache L2
-#define EVENT_SEL_BSQ_cache_reference 0x0c
-#define EVENT_MASK_BSQ_cache_reference_RD_L2_HITS 0x001
-#define EVENT_MASK_BSQ_cache_reference_RD_L2_HITE 0x002
-#define EVENT_MASK_BSQ_cache_reference_RD_L2_HITM 0x004
-
-#define EVENT_MASK_BSQ_cache_reference_RD_L3_HITS 0x008
-#define EVENT_MASK_BSQ_cache_reference_RD_L3_HITE 0x010
-#define EVENT_MASK_BSQ_cache_reference_RD_L3_HITM 0x020
-
-#define EVENT_MASK_BSQ_cache_reference_RD_L2_MISS 0x100
-#define EVENT_MASK_BSQ_cache_reference_RD_L3_MISS 0x200
-#define EVENT_MASK_BSQ_cache_reference_WR_L2_MISS 0x400
-
-/*****************************************************************************
- * *
- *****************************************************************************/
-
-
-/* The following turn configuration macros into 1/0 to allow code to be
- * selected using if(MPENTIUM4_HT) rather then #ifdef (to avoid stale code).
- * We rely on the compiler to optimise out unreachable code,
- */
-#ifdef CONFIG_MPENTIUM4_HT
-# define MPENTIUM4_HT 1
-#else
-# define MPENTIUM4_HT 0
-#endif
-
-#ifdef CONFIG_MPENTIUMIII
-# define MPENTIUMIII 1
-#else
-# define MPENTIUMIII 0
-#endif
-
-#ifdef CONFIG_MPENTIUM4
-# define MPENTIUM4 1
-#else
-# define MPENTIUM4 0
-#endif
-
-/*****************************************************************************
- * MSR access macros *
- *****************************************************************************/
-
-/* rpcc: get full 64-bit Pentium TSC value
- */
-static __inline__ unsigned long long int rpcc(void)
-{
- unsigned int __h, __l;
- __asm__ __volatile__ ("rdtsc" :"=a" (__l), "=d" (__h));
- return (((unsigned long long)__h) << 32) + __l;
-}
-
-/*****************************************************************************
- * Functions. *
- *****************************************************************************/
-
-#ifdef __KERNEL__
-static inline void smt_sched_setup(void)
-{
- if (MPENTIUMIII) {
- unsigned int evntsel, x;
-
- /* Make sure counters enabled. */
- rdmsr(MSR_P6_EVNTSEL0, evntsel, x);
- evntsel |= P6_EVNTSEL_EN;
- wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
-
- evntsel =
- P6_PERF_INST_RETIRED |
- P6_EVNTSEL_OS |
- P6_EVNTSEL_USR |
- P6_EVNTSEL_E;
- wrmsr(MSR_P6_EVNTSEL1, evntsel, 0);
- }
-
- if(MPENTIUM4) {
- unsigned int x;
-
- /* Program the ESCR */
- x = P4_ESCR_USR |
- P4_ESCR_OS |
- P4_ESCR_EVNTSEL(P4_ESCR_EVNTSEL_INSTR_RETIRED) |
- P4_ESCR_EVNTMASK(P4_ESCR_EVNTMASK_IRET_NB_NTAG);
- wrmsr(MSR_P4_CRU_ESCR0, x, 0);
-
- /* Program the CCCR */
- if (MPENTIUM4_HT) {
- x = P4_CCCR_ENABLE |
- P4_CCCR_ESCR(P4_CRU_ESCR0_NUMBER) |
- P4_CCCR_ACTIVE_THREAD(3);
- }
- else {
- x = P4_CCCR_ENABLE |
- P4_CCCR_ESCR(P4_CRU_ESCR0_NUMBER) |
- P4_CCCR_RESERVED;
- }
- wrmsr(MSR_P4_IQ_CCCR0, x, 0);
-
- if (MPENTIUM4_HT) {
-
- /* Program the second ESCR */
- x = P4_ESCR_T1_USR |
- P4_ESCR_T1_OS |
- P4_ESCR_EVNTSEL(P4_ESCR_EVNTSEL_INSTR_RETIRED) |
- P4_ESCR_EVNTMASK(P4_ESCR_EVNTMASK_IRET_NB_NTAG);
- wrmsr(MSR_P4_CRU_ESCR1, x, 0);
-
- /* Program the second CCCR */
- x = P4_CCCR_ENABLE |
- P4_CCCR_ESCR(P4_CRU_ESCR1_NUMBER) |
- P4_CCCR_ACTIVE_THREAD(3);
- wrmsr(MSR_P4_IQ_CCCR2, x, 0);
- }
- }
-
- if (!MPENTIUMIII && !MPENTIUM4) {
- printk("WARNING: Not setting up IPC performance counters.\n");
- } else {
- printk("Setting up IPC performance counters.\n");
- }
-}
-
-#ifdef CONFIG_MPENTIUMIII
-# define MY_MSR_COUNTER MSR_P6_PERFCTR1
-#endif
-#ifdef CONFIG_MPENTIUM4
-# define MY_MSR_COUNTER MSR_P4_IQ_COUNTER0
-#endif
-#ifndef MY_MSR_COUNTER
-# define MY_MSR_COUNTER 0 /* Never used but ensures compilation */
-#endif
-#define MY_MSR_COUNTER0 MSR_P4_IQ_COUNTER0
-#define MY_MSR_COUNTER1 MSR_P4_IQ_COUNTER2
-
-# define smt_sched_start_sample(task) \
-{ \
- unsigned int l, h; \
- \
- if (MPENTIUM4_HT) { \
- unsigned int msr = \
- (task->processor & 1)?MY_MSR_COUNTER1:MY_MSR_COUNTER0; \
- rdmsr(msr, l, h); \
- } \
- else { \
- rdmsr(MY_MSR_COUNTER, l, h); \
- } \
- task->ipc_sample_start_count_lo = l; \
- task->ipc_sample_start_count_hi = h; \
- rdtsc(l, h); \
- task->ipc_sample_start_cycle_lo = l; \
- task->ipc_sample_start_cycle_hi = h; \
-}
-
-# define smt_sched_stop_sample(task) \
-{ \
- if (task->ipc_sample_start_cycle_hi != 0) \
- { \
- unsigned int cl, ch, tl, th; \
- unsigned int c, t; \
- \
- if (MPENTIUM4_HT) { \
- unsigned int msr = \
- (task->processor & 1)?MY_MSR_COUNTER1:MY_MSR_COUNTER0; \
- rdmsr(msr, cl, ch); \
- } \
- else { \
- rdmsr(MY_MSR_COUNTER, cl, ch); \
- } \
- \
- rdtsc(tl, th); \
- \
- c = cl - task->ipc_sample_start_count_lo; \
- t = tl - task->ipc_sample_start_cycle_lo; \
- task->ipc_average = IPC_AVERAGE(task->ipc_average, \
- ((double)c)/((double)t)); \
- task->ipc_sample_start_cycle_hi = 0; \
- \
- } \
- else \
- task->ipc_average = 0.0; \
- \
-}
-
-// task->ipc_sample_latest =
-// (unsigned int)(1000.0*((double)c)/((double)t));
-#endif /* __KERNEL__ */
-
-
-#endif /* P4PERF_H */
-
-/* End of $RCSfile$ */
diff --git a/tools/misc/xen-clone b/tools/misc/xen-clone
index b5e857f183..3fac8109f1 100755
--- a/tools/misc/xen-clone
+++ b/tools/misc/xen-clone
@@ -17,12 +17,13 @@ UCCL)
*)
BK_REP=${1:-bk://xen.bkbits.net/xeno-1.0.bk}
# BK_REP=${1:-ssh://xen@xen.bkbits.net/xeno-1.0.bk}
- LINUX_DIR=${3:-..}
+ LINUX_DIR=${3:-.:..}
;;
esac
DEST_DIR=${2:-xeno-clone}
DEST_BK_REP=`basename "${BK_REP}"`
+DEST_VER=`basename ${DEST_BK_REP} .bk`
echo usage: xen-clone bk_repository dest_dir orig_linux_dir
echo Source BK Repository : ${BK_REP}
@@ -65,18 +66,23 @@ if [ -d ${DEST_BK_REP}/linux-2.4.*-xen-sparse ]
then
# this is a new style Xen repository so building is dead easy
- LINUX_VER=`( /bin/ls -ld ${DEST_BK_REP}/*linux-2.4.*-xen-sparse ) 2>/dev/null | sed -e 's!^.*linux-\(.\+\)-xen-sparse!\1!'`
+ export LINUX_SRC_PATH=${LINUX_DIR}
- if [ -e ${LINUX_DIR}/linux-${LINUX_VER}.tar.gz ]
+ cd ${DEST_BK_REP}
+
+ # Recent repositories install into 'dist/install' rather than 'install'.
+ if [ -f install.sh ]
then
- export LINUX_SRC=${LINUX_DIR}/linux-${LINUX_VER}.tar.gz
+ mkdir -p dist
+ ln -sf ../../install dist/install
+ else
+ ln -sf ../install install
fi
- cd ${DEST_BK_REP}
- ln -sf ../install install
make -j4 world
+ make -j4 linux24
cd ../install/boot
- [ -r vmlinuz-${LINUX_VER}-xen0 ] && ln -s vmlinuz-${LINUX_VER}-xen0 xenolinux.gz
+ [ -r vmlinuz-2.6.*-xen0 ] && ln -s vmlinuz-2.6.*-xen0 xenolinux.gz
else
# old style repository without 'make world'
diff --git a/tools/misc/xen_cpuperf.c b/tools/misc/xen_cpuperf.c
deleted file mode 100644
index 235ca58a39..0000000000
--- a/tools/misc/xen_cpuperf.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * User mode program to prod MSR values through /proc/perfcntr
- */
-
-#include <sys/types.h>
-#include <sched.h>
-#include <error.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "p4perf.h"
-#include "xc_private.h"
-
-void dom0_wrmsr( int privfd,
- int cpu_mask,
- int msr,
- unsigned int low,
- unsigned int high )
-{
- dom0_op_t op;
- op.cmd = DOM0_MSR;
- op.u.msr.write = 1;
- op.u.msr.msr = msr;
- op.u.msr.cpu_mask = cpu_mask;
- op.u.msr.in1 = low;
- op.u.msr.in2 = high;
- do_dom0_op(privfd, &op);
-}
-
-unsigned long long dom0_rdmsr( int privfd,
- int cpu_mask,
- int msr )
-{
- dom0_op_t op;
- op.cmd = DOM0_MSR;
- op.u.msr.write = 0;
- op.u.msr.msr = msr;
- op.u.msr.cpu_mask = cpu_mask;
- do_dom0_op(privfd, &op);
- return (((unsigned long long)op.u.msr.out2)<<32) | op.u.msr.out1 ;
-}
-
-struct macros {
- char *name;
- unsigned long msr_addr;
- int number;
-};
-
-struct macros msr[] = {
- {"BPU_COUNTER0", 0x300, 0},
- {"BPU_COUNTER1", 0x301, 1},
- {"BPU_COUNTER2", 0x302, 2},
- {"BPU_COUNTER3", 0x303, 3},
- {"MS_COUNTER0", 0x304, 4},
- {"MS_COUNTER1", 0x305, 5},
- {"MS_COUNTER2", 0x306, 6},
- {"MS_COUNTER3", 0x307, 7},
- {"FLAME_COUNTER0", 0x308, 8},
- {"FLAME_COUNTER1", 0x309, 9},
- {"FLAME_COUNTER2", 0x30a, 10},
- {"FLAME_COUNTER3", 0x30b, 11},
- {"IQ_COUNTER0", 0x30c, 12},
- {"IQ_COUNTER1", 0x30d, 13},
- {"IQ_COUNTER2", 0x30e, 14},
- {"IQ_COUNTER3", 0x30f, 15},
- {"IQ_COUNTER4", 0x310, 16},
- {"IQ_COUNTER5", 0x311, 17},
- {"BPU_CCCR0", 0x360, 0},
- {"BPU_CCCR1", 0x361, 1},
- {"BPU_CCCR2", 0x362, 2},
- {"BPU_CCCR3", 0x363, 3},
- {"MS_CCCR0", 0x364, 4},
- {"MS_CCCR1", 0x365, 5},
- {"MS_CCCR2", 0x366, 6},
- {"MS_CCCR3", 0x367, 7},
- {"FLAME_CCCR0", 0x368, 8},
- {"FLAME_CCCR1", 0x369, 9},
- {"FLAME_CCCR2", 0x36a, 10},
- {"FLAME_CCCR3", 0x36b, 11},
- {"IQ_CCCR0", 0x36c, 12},
- {"IQ_CCCR1", 0x36d, 13},
- {"IQ_CCCR2", 0x36e, 14},
- {"IQ_CCCR3", 0x36f, 15},
- {"IQ_CCCR4", 0x370, 16},
- {"IQ_CCCR5", 0x371, 17},
- {"BSU_ESCR0", 0x3a0, 7},
- {"BSU_ESCR1", 0x3a1, 7},
- {"FSB_ESCR0", 0x3a2, 6},
- {"FSB_ESCR1", 0x3a3, 6},
- {"MOB_ESCR0", 0x3aa, 2},
- {"MOB_ESCR1", 0x3ab, 2},
- {"PMH_ESCR0", 0x3ac, 4},
- {"PMH_ESCR1", 0x3ad, 4},
- {"BPU_ESCR0", 0x3b2, 0},
- {"BPU_ESCR1", 0x3b3, 0},
- {"IS_ESCR0", 0x3b4, 1},
- {"IS_ESCR1", 0x3b5, 1},
- {"ITLB_ESCR0", 0x3b6, 3},
- {"ITLB_ESCR1", 0x3b7, 3},
- {"IX_ESCR0", 0x3c8, 5},
- {"IX_ESCR1", 0x3c9, 5},
- {"MS_ESCR0", 0x3c0, 0},
- {"MS_ESCR1", 0x3c1, 0},
- {"TBPU_ESCR0", 0x3c2, 2},
- {"TBPU_ESCR1", 0x3c3, 2},
- {"TC_ESCR0", 0x3c4, 1},
- {"TC_ESCR1", 0x3c5, 1},
- {"FIRM_ESCR0", 0x3a4, 1},
- {"FIRM_ESCR1", 0x3a5, 1},
- {"FLAME_ESCR0", 0x3a6, 0},
- {"FLAME_ESCR1", 0x3a7, 0},
- {"DAC_ESCR0", 0x3a8, 5},
- {"DAC_ESCR1", 0x3a9, 5},
- {"SAAT_ESCR0", 0x3ae, 2},
- {"SAAT_ESCR1", 0x3af, 2},
- {"U2L_ESCR0", 0x3b0, 3},
- {"U2L_ESCR1", 0x3b1, 3},
- {"CRU_ESCR0", 0x3b8, 4},
- {"CRU_ESCR1", 0x3b9, 4},
- {"CRU_ESCR2", 0x3cc, 5},
- {"CRU_ESCR3", 0x3cd, 5},
- {"CRU_ESCR4", 0x3e0, 6},
- {"CRU_ESCR5", 0x3e1, 6},
- {"IQ_ESCR0", 0x3ba, 0},
- {"IQ_ESCR1", 0x3bb, 0},
- {"RAT_ESCR0", 0x3bc, 2},
- {"RAT_ESCR1", 0x3bd, 2},
- {"SSU_ESCR0", 0x3be, 3},
- {"SSU_ESCR1", 0x3bf, 3},
- {"ALF_ESCR0", 0x3ca, 1},
- {"ALF_ESCR1", 0x3cb, 1},
- {"PEBS_ENABLE", 0x3f1, 0},
- {"PEBS_MATRIX_VERT", 0x3f2, 0},
- {NULL, 0, 0}
-};
-
-struct macros *lookup_macro(char *str)
-{
- struct macros *m;
-
- m = msr;
- while (m->name) {
- if (strcmp(m->name, str) == 0)
- return m;
- m++;
- }
- return NULL;
-}
-
-int main(int argc, char **argv)
-{
- int c, t = 0xc, es = 0, em = 0, tv = 0, te = 0;
- unsigned int cpu_mask = 1;
- struct macros *escr = NULL, *cccr = NULL;
- unsigned long escr_val, cccr_val;
- int debug = 0;
- unsigned long pebs = 0, pebs_vert = 0;
- int pebs_x = 0, pebs_vert_x = 0;
- int read = 0, privfd;
-
- while ((c = getopt(argc, argv, "dc:t:e:m:T:E:C:P:V:r")) != -1) {
- switch((char)c) {
- case 'P':
- pebs |= 1 << atoi(optarg);
- pebs_x = 1;
- break;
- case 'V':
- pebs_vert |= 1 << atoi(optarg);
- pebs_vert_x = 1;
- break;
- case 'd':
- debug = 1;
- break;
- case 'c':
- {
- int cpu = atoi(optarg);
- cpu_mask = (cpu == -1)?(~0):(1<<cpu);
- break;
- }
- case 't': // ESCR thread bits
- t = atoi(optarg);
- break;
- case 'e': // eventsel
- es = atoi(optarg);
- break;
- case 'm': // eventmask
- em = atoi(optarg);
- break;
- case 'T': // tag value
- tv = atoi(optarg);
- te = 1;
- break;
- case 'E':
- escr = lookup_macro(optarg);
- if (!escr) {
- fprintf(stderr, "Macro '%s' not found.\n", optarg);
- exit(1);
- }
- break;
- case 'C':
- cccr = lookup_macro(optarg);
- if (!cccr) {
- fprintf(stderr, "Macro '%s' not found.\n", optarg);
- exit(1);
- }
- break;
- case 'r':
- read = 1;
- break;
- }
- }
-
- if ( (privfd = open("/proc/xen/privcmd", O_RDWR)) == -1 )
- {
- fprintf(stderr, "Could not open privileged Xen control interface.\n");
- exit(1);
- }
-
- if (read) {
- while((cpu_mask&1)) {
- int i;
- for (i=0x300;i<0x312;i++)
- {
- printf("%010llx ",dom0_rdmsr( privfd, cpu_mask, i ) );
- }
- printf("\n");
- cpu_mask>>=1;
- }
- exit(1);
- }
-
- if (!escr) {
- fprintf(stderr, "Need an ESCR.\n");
- exit(1);
- }
- if (!cccr) {
- fprintf(stderr, "Need a counter number.\n");
- exit(1);
- }
-
- escr_val = P4_ESCR_THREADS(t) | P4_ESCR_EVNTSEL(es) |
- P4_ESCR_EVNTMASK(em) | P4_ESCR_TV(tv) | ((te)?P4_ESCR_TE:0);
- cccr_val = P4_CCCR_ENABLE | P4_CCCR_ESCR(escr->number) |
- P4_CCCR_ACTIVE_THREAD(3)/*reserved*/;
-
- if (debug) {
- fprintf(stderr, "ESCR 0x%lx <= 0x%08lx\n", escr->msr_addr, escr_val);
- fprintf(stderr, "CCCR 0x%lx <= 0x%08lx (%u)\n",
- cccr->msr_addr, cccr_val, cccr->number);
- if (pebs_x)
- fprintf(stderr, "PEBS 0x%x <= 0x%08lx\n",
- MSR_P4_PEBS_ENABLE, pebs);
- if (pebs_vert_x)
- fprintf(stderr, "PMV 0x%x <= 0x%08lx\n",
- MSR_P4_PEBS_MATRIX_VERT, pebs_vert);
- }
-
- dom0_wrmsr( privfd, cpu_mask, escr->msr_addr, escr_val, 0 );
- dom0_wrmsr( privfd, cpu_mask, cccr->msr_addr, cccr_val, 0 );
-
- if (pebs_x)
- dom0_wrmsr( privfd, cpu_mask, MSR_P4_PEBS_ENABLE, pebs, 0 );
-
- if (pebs_vert_x)
- dom0_wrmsr( privfd, cpu_mask, MSR_P4_PEBS_MATRIX_VERT, pebs_vert, 0 );
-
- return 0;
-}
-
diff --git a/tools/misc/xencons b/tools/misc/xencons
index 434fce1e8b..8bd3178eab 100755
--- a/tools/misc/xencons
+++ b/tools/misc/xencons
@@ -27,26 +27,34 @@ def __recv_from_sock(sock):
while not stop:
try:
data = sock.recv(1024)
- if len(data) == 0:
- stop = True
- os.write(1, data)
except socket.error, error:
if error[0] != errno.EINTR:
raise
+ else:
+ try:
+ os.write(1, data)
+ except os.error, error:
+ if error[0] != errno.EINTR:
+ raise
os.wait()
def __send_to_sock(sock):
while 1:
- data = os.read(0,1024)
- if ord(data[0]) == ord(']')-64:
- break
try:
- sock.send(data)
- except socket.error, error:
- if error[0] == errno.EPIPE:
- sys.exit(0)
+ data = os.read(0,1024)
+ except os.error, error:
if error[0] != errno.EINTR:
raise
+ else:
+ if ord(data[0]) == ord(']')-64:
+ break
+ try:
+ sock.send(data)
+ except socket.error, error:
+ if error[0] == errno.EPIPE:
+ sys.exit(0)
+ if error[0] != errno.EINTR:
+ raise
sys.exit(0)
def connect(host,port):
diff --git a/tools/misc/xend b/tools/misc/xend
index d04e5c77df..5c9757ec23 100644
--- a/tools/misc/xend
+++ b/tools/misc/xend
@@ -8,11 +8,12 @@
Provides console server and HTTP management api.
Run:
-
xend start
- The daemon is stopped with:
+ Restart:
+ xend restart
+ The daemon is stopped with:
xend stop
The daemon should reconnect to device control interfaces
@@ -20,6 +21,16 @@
"""
import os
import sys
+import socket
+import time
+
+XCS_PORT = 1633
+XCS_EXEC = "/usr/sbin/xcs"
+XCS_LOGFILE = "/var/log/xcs.log"
+
+# Default install path for Xen binary packages.
+sys.path = [ '/usr/lib/python' ] + sys.path
+from xen.xend.server import SrvDaemon
class CheckError(ValueError):
pass
@@ -84,6 +95,18 @@ def check_user():
msg("Xend must be run as root.")
hline()
raise CheckError("invalid user")
+
+def xcs_running():
+ """ See if the control switch is running.
+ """
+ ret = 1
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ try:
+ s.connect( ("127.0.0.1", XCS_PORT) )
+ except:
+ ret = 0
+ s.close()
+ return (ret)
def main():
try:
@@ -92,7 +115,31 @@ def main():
check_user()
except CheckError:
sys.exit(1)
- from xen.xend.server import SrvDaemon
+
+ if (not xcs_running()):
+ if os.fork():
+ time.sleep(1) # let xcs start
+ else:
+ try:
+ logfile = os.open(XCS_LOGFILE,
+ os.O_WRONLY|os.O_APPEND|os.O_CREAT)
+ os.close(1)
+ os.dup(logfile)
+ os.close(2)
+ os.dup(logfile)
+ os.close(logfile)
+ os.execlp(XCS_EXEC, XCS_EXEC)
+ except:
+ hline()
+ msg("Tried to start xcs, but failed. Is it installed?")
+ hline()
+ raise CheckError("couldn't start xcs")
+ if (not xcs_running()):
+ hline()
+ msg("Failed to start the control interface switch.")
+ hline()
+ raise CheckError("xcs not running")
+
daemon = SrvDaemon.instance()
if not sys.argv[1:]:
print 'usage: %s {start|stop|restart}' % sys.argv[0]
@@ -107,6 +154,8 @@ def main():
return daemon.stop()
elif sys.argv[1] == 'restart':
return daemon.stop() or daemon.start()
+ elif sys.argv[1] == 'status':
+ return daemon.status()
else:
print 'not an option:', sys.argv[1]
return 1
diff --git a/tools/misc/xenperf.c b/tools/misc/xenperf.c
new file mode 100644
index 0000000000..d2846224b1
--- /dev/null
+++ b/tools/misc/xenperf.c
@@ -0,0 +1,104 @@
+/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
+ ****************************************************************************
+ * (C) 2004 - Rolf Neugebauer - Intel Research Cambridge
+ ****************************************************************************
+ *
+ * File: xenperf.c
+ * Author: Rolf Neugebauer (rolf.neugebauer@intel.com)
+ * Date: Nov 2004
+ *
+ * Description:
+ */
+
+
+#include <xc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ int i, j, xc_handle;
+ xc_perfc_desc_t *pcd;
+ unsigned int num, sum, reset = 0;
+
+ if ( argc > 1 )
+ {
+ char *p = argv[1];
+ if ( (*p++ == '-') && (*p == 'r') )
+ reset = 1;
+ else
+ {
+ printf("%s: [-r]\n", argv[0]);
+ printf("no args: print xen performance counters\n");
+ printf(" -r : reset xen performance counters\n");
+ return 0;
+ }
+ }
+
+ if ( (xc_handle = xc_interface_open()) == -1 )
+ {
+ fprintf(stderr, "Error opening xc interface: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ if ( reset )
+ {
+ if ( xc_perfc_control(xc_handle, DOM0_PERFCCONTROL_OP_RESET,
+ NULL) < 0 )
+ {
+ fprintf(stderr, "Error reseting performance counters: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ return 0;
+ }
+
+
+ if ( (num = xc_perfc_control(xc_handle, DOM0_PERFCCONTROL_OP_QUERY,
+ NULL)) < 0 )
+ {
+ fprintf(stderr, "Error getting number of perf counters: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ pcd = malloc(sizeof(*pcd) * num);
+
+ if ( mlock(pcd, sizeof(*pcd) * num) != 0 )
+ {
+ fprintf(stderr, "Could not mlock descriptor buffer: %d (%s)\n",
+ errno, strerror(errno));
+ exit(-1);
+ }
+
+ if ( xc_perfc_control(xc_handle, DOM0_PERFCCONTROL_OP_QUERY, pcd) <= 0 )
+ {
+ fprintf(stderr, "Error getting perf counter description: %d (%s)\n",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ munlock(pcd, sizeof(*pcd) * num);
+
+ for ( i = 0; i < num; i++ )
+ {
+ printf ("%-35s ", pcd[i].name);
+
+ sum = 0;
+ for ( j = 0; j < pcd[i].nr_vals; j++ )
+ sum += pcd[i].vals[j];
+ printf ("T=%10u ", (unsigned int)sum);
+
+ for ( j = 0; j < pcd[i].nr_vals; j++ )
+ printf(" %10u", (unsigned int)pcd[i].vals[j]);
+
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/tools/misc/xensv b/tools/misc/xensv
index b877538d67..7924840615 100755
--- a/tools/misc/xensv
+++ b/tools/misc/xensv
@@ -22,7 +22,9 @@ import os
import sys
import re
-from xen.xend.server.params import PID_FILE as XEND_PID_FILE
+# Default install path for Xen binary packages.
+sys.path = [ '/usr/lib/python' ] + sys.path
+from xen.xend.server.params import XEND_PID_FILE
class CheckError(ValueError):
pass
diff --git a/tools/misc/xensymoops.py b/tools/misc/xensymoops
index 835d187e90..835d187e90 100755
--- a/tools/misc/xensymoops.py
+++ b/tools/misc/xensymoops
diff --git a/tools/misc/xm b/tools/misc/xm
index 6ace888cda..86e45573d0 100755
--- a/tools/misc/xm
+++ b/tools/misc/xm
@@ -1,6 +1,9 @@
#!/usr/bin/env python
# -*- mode: python; -*-
import sys
+
+# Default install path for Xen binary packages.
+sys.path = [ '/usr/lib/python' ] + sys.path
from xen.xm import main
main.main(sys.argv)
diff --git a/tools/python/Makefile b/tools/python/Makefile
index 7dc74a1072..7d152e12ae 100644
--- a/tools/python/Makefile
+++ b/tools/python/Makefile
@@ -1,15 +1,12 @@
+XEN_ROOT = ../..
+include $(XEN_ROOT)/tools/Rules.mk
+
all:
- python setup.py build
+ CFLAGS="$(CFLAGS)" python setup.py build
install: all
- if [ "$(prefix)" = "" ]; then \
- python setup.py install; \
- elif [ "$(dist)" = "yes" ]; then \
- python setup.py install --home="$(prefix)"; \
- else \
- python setup.py install --root="$(prefix)"; \
- fi
+ CFLAGS="$(CFLAGS)" python setup.py install --home="$(DESTDIR)/usr"
clean:
rm -rf build *.pyc *.pyo *.o *.a *~
diff --git a/tools/python/setup.py b/tools/python/setup.py
index 7adae6cc94..81536989ee 100644
--- a/tools/python/setup.py
+++ b/tools/python/setup.py
@@ -7,11 +7,10 @@ XEN_ROOT = "../.."
extra_compile_args = [ "-fno-strict-aliasing", "-Wall", "-Werror" ]
-include_dirs = [ XEN_ROOT + "/xen/include/hypervisor-ifs",
- XEN_ROOT + "/linux-xen-sparse/include",
- XEN_ROOT + "/tools/python/xen/lowlevel/xu",
+include_dirs = [ XEN_ROOT + "/tools/python/xen/lowlevel/xu",
XEN_ROOT + "/tools/libxc",
XEN_ROOT + "/tools/libxutil",
+ XEN_ROOT + "/tools/xcs",
]
library_dirs = [ XEN_ROOT + "/tools/libxc",
diff --git a/tools/python/xen/lowlevel/xc/xc.c b/tools/python/xen/lowlevel/xc/xc.c
index 4b5a426b55..543a2cd380 100644
--- a/tools/python/xen/lowlevel/xc/xc.c
+++ b/tools/python/xen/lowlevel/xc/xc.c
@@ -16,6 +16,7 @@
#include <arpa/inet.h>
#include "xc_private.h"
#include "gzip_stream.h"
+#include "linux_boot_params.h"
/* Needed for Python versions earlier than 2.3. */
#ifndef PyMODINIT_FUNC
@@ -42,18 +43,19 @@ static PyObject *pyxc_domain_create(PyObject *self,
XcObject *xc = (XcObject *)self;
unsigned int mem_kb = 0;
- char *name = "(anon)";
int cpu = -1;
+ float cpu_weight = 1;
u32 dom = 0;
int ret;
- static char *kwd_list[] = { "dom", "mem_kb", "name", "cpu", NULL };
+ static char *kwd_list[] = { "dom", "mem_kb", "cpu", "cpu_weight", NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iisi", kwd_list,
- &dom, &mem_kb, &name, &cpu) )
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiif", kwd_list,
+ &dom, &mem_kb, &cpu, &cpu_weight))
return NULL;
- if ( (ret = xc_domain_create(xc->xc_handle, mem_kb, name, cpu, &dom)) < 0 )
+ if ( (ret = xc_domain_create(
+ xc->xc_handle, mem_kb, cpu, cpu_weight, &dom)) < 0 )
return PyErr_SetFromErrno(xc_error);
return PyInt_FromLong(dom);
@@ -169,7 +171,7 @@ static PyObject *pyxc_domain_getinfo(PyObject *self,
PyList_SetItem(
list, i,
Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i"
- ",s:l,s:L,s:s,s:l,s:i}",
+ ",s:l,s:L,s:l,s:i}",
"dom", info[i].domid,
"cpu", info[i].cpu,
"dying", info[i].dying,
@@ -180,7 +182,6 @@ static PyObject *pyxc_domain_getinfo(PyObject *self,
"running", info[i].running,
"mem_kb", info[i].nr_pages*4,
"cpu_time", info[i].cpu_time,
- "name", info[i].name,
"maxmem_kb", info[i].max_memkb,
"shutdown_reason", info[i].shutdown_reason
));
@@ -347,45 +348,130 @@ static PyObject *pyxc_linux_build(PyObject *self,
u32 dom;
char *image, *ramdisk = NULL, *cmdline = "";
- int control_evtchn, flags = 0;
+ int control_evtchn, flags = 0, vcpus = 1;
static char *kwd_list[] = { "dom", "control_evtchn",
- "image", "ramdisk", "cmdline", "flags",
+ "image", "ramdisk", "cmdline", "flags", "vcpus",
NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|ssi", kwd_list,
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|ssii", kwd_list,
&dom, &control_evtchn,
- &image, &ramdisk, &cmdline, &flags) )
+ &image, &ramdisk, &cmdline, &flags, &vcpus) )
return NULL;
if ( xc_linux_build(xc->xc_handle, dom, image,
- ramdisk, cmdline, control_evtchn, flags) != 0 )
+ ramdisk, cmdline, control_evtchn, flags, vcpus) != 0 )
return PyErr_SetFromErrno(xc_error);
Py_INCREF(zero);
return zero;
}
-static PyObject *pyxc_netbsd_build(PyObject *self,
- PyObject *args,
- PyObject *kwds)
+static PyObject *pyxc_plan9_build(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
{
XcObject *xc = (XcObject *)self;
u32 dom;
char *image, *ramdisk = NULL, *cmdline = "";
- int control_evtchn;
+ int control_evtchn, flags = 0;
static char *kwd_list[] = { "dom", "control_evtchn",
- "image", "ramdisk", "cmdline", NULL };
+ "image", "ramdisk", "cmdline", "flags",
+ NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|ssi", kwd_list,
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iis|ssi", kwd_list,
&dom, &control_evtchn,
- &image, &ramdisk, &cmdline) )
+ &image, &ramdisk, &cmdline, &flags) )
return NULL;
- if ( xc_netbsd_build(xc->xc_handle, dom, image,
- cmdline, control_evtchn) != 0 )
+ if ( xc_plan9_build(xc->xc_handle, dom, image,
+ cmdline, control_evtchn, flags) != 0 )
+ return PyErr_SetFromErrno(xc_error);
+
+ Py_INCREF(zero);
+ return zero;
+}
+
+static PyObject *pyxc_vmx_build(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
+{
+ XcObject *xc = (XcObject *)self;
+
+ u32 dom;
+ char *image, *ramdisk = NULL, *cmdline = "";
+ PyObject *memmap;
+ int control_evtchn, flags = 0;
+ int numItems, i;
+ int memsize;
+ struct mem_map mem_map;
+
+ static char *kwd_list[] = { "dom", "control_evtchn",
+ "memsize",
+ "image", "memmap",
+ "ramdisk", "cmdline", "flags",
+ NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiisO!|ssi", kwd_list,
+ &dom, &control_evtchn,
+ &memsize,
+ &image, &PyList_Type, &memmap,
+ &ramdisk, &cmdline, &flags) )
+ return NULL;
+
+ memset(&mem_map, 0, sizeof(mem_map));
+ /* Parse memmap */
+
+ /* get the number of lines passed to us */
+ numItems = PyList_Size(memmap) - 1; /* removing the line
+ containing "memmap" */
+ printf ("numItems: %d\n", numItems);
+ mem_map.nr_map = numItems;
+
+
+ /* should raise an error here. */
+ if (numItems < 0) return NULL; /* Not a list */
+
+
+ /* iterate over items of the list, grabbing ranges and parsing them */
+ for (i = 1; i <= numItems; i++) { // skip over "memmap"
+ PyObject *item, *f1, *f2, *f3, *f4;
+ int numFields;
+ unsigned long lf1, lf2, lf3, lf4;
+ char *sf1, *sf2;
+
+ /* grab the string object from the next element of the list */
+ item = PyList_GetItem(memmap, i); /* Can't fail */
+
+ /* get the number of lines passed to us */
+ numFields = PyList_Size(item);
+
+ if (numFields != 4)
+ return NULL;
+
+ f1 = PyList_GetItem(item, 0);
+ f2 = PyList_GetItem(item, 1);
+ f3 = PyList_GetItem(item, 2);
+ f4 = PyList_GetItem(item, 3);
+
+ /* Convert objects to strings/longs */
+ sf1 = PyString_AsString(f1);
+ sf2 = PyString_AsString(f2);
+ lf3 = PyLong_AsLong(f3);
+ lf4 = PyLong_AsLong(f4);
+ sscanf(sf1, "%lx", &lf1);
+ sscanf(sf2, "%lx", &lf2);
+
+ mem_map.map[i-1].addr = lf1;
+ mem_map.map[i-1].size = lf2 - lf1;
+ mem_map.map[i-1].type = lf3;
+ mem_map.map[i-1].caching_attr = lf4;
+ }
+
+ if ( xc_vmx_build(xc->xc_handle, dom, memsize, image, &mem_map,
+ ramdisk, cmdline, control_evtchn, flags) != 0 )
return PyErr_SetFromErrno(xc_error);
Py_INCREF(zero);
@@ -488,90 +574,24 @@ static PyObject *pyxc_bvtsched_domain_get(PyObject *self,
"warpu", warpu);
}
-static PyObject *pyxc_fbvtsched_global_set(PyObject *self,
- PyObject *args,
- PyObject *kwds)
-{
- XcObject *xc = (XcObject *)self;
-
- unsigned long ctx_allow;
-
- static char *kwd_list[] = { "ctx_allow", NULL };
-
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "l", kwd_list, &ctx_allow) )
- return NULL;
-
- if ( xc_fbvtsched_global_set(xc->xc_handle, ctx_allow) != 0 )
- return PyErr_SetFromErrno(xc_error);
-
- Py_INCREF(zero);
- return zero;
-}
-
-static PyObject *pyxc_fbvtsched_global_get(PyObject *self,
- PyObject *args,
- PyObject *kwds)
-{
- XcObject *xc = (XcObject *)self;
-
- unsigned long ctx_allow;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( xc_fbvtsched_global_get(xc->xc_handle, &ctx_allow) != 0 )
- return PyErr_SetFromErrno(xc_error);
-
- return Py_BuildValue("s:l", "ctx_allow", ctx_allow);
-}
-
-static PyObject *pyxc_fbvtsched_domain_set(PyObject *self,
- PyObject *args,
- PyObject *kwds)
+static PyObject *pyxc_evtchn_alloc_unbound(PyObject *self,
+ PyObject *args,
+ PyObject *kwds)
{
XcObject *xc = (XcObject *)self;
- u32 dom;
- unsigned long mcuadv, warp, warpl, warpu;
-
- static char *kwd_list[] = { "dom", "mcuadv", "warp", "warpl",
- "warpu", NULL };
-
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "illll", kwd_list,
- &dom, &mcuadv, &warp, &warpl, &warpu) )
- return NULL;
-
- if ( xc_fbvtsched_domain_set(xc->xc_handle, dom, mcuadv,
- warp, warpl, warpu) != 0 )
- return PyErr_SetFromErrno(xc_error);
-
- Py_INCREF(zero);
- return zero;
-}
-
-static PyObject *pyxc_fbvtsched_domain_get(PyObject *self,
- PyObject *args,
- PyObject *kwds)
-{
- XcObject *xc = (XcObject *)self;
u32 dom;
- unsigned long mcuadv, warp, warpl, warpu;
-
+ int port;
+
static char *kwd_list[] = { "dom", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) )
return NULL;
-
- if ( xc_fbvtsched_domain_get(xc->xc_handle, dom, &mcuadv, &warp,
- &warpl, &warpu) != 0 )
+
+ if ( xc_evtchn_alloc_unbound(xc->xc_handle, dom, &port) != 0 )
return PyErr_SetFromErrno(xc_error);
- return Py_BuildValue("{s:i,s:l,s:l,s:l,s:l}",
- "domain", dom,
- "mcuadv", mcuadv,
- "warp", warp,
- "warpl", warpl,
- "warpu", warpu);
+ return PyInt_FromLong(port);
}
static PyObject *pyxc_evtchn_bind_interdomain(PyObject *self,
@@ -581,12 +601,12 @@ static PyObject *pyxc_evtchn_bind_interdomain(PyObject *self,
XcObject *xc = (XcObject *)self;
u32 dom1 = DOMID_SELF, dom2 = DOMID_SELF;
- int port1, port2;
+ int port1 = 0, port2 = 0;
- static char *kwd_list[] = { "dom1", "dom2", NULL };
+ static char *kwd_list[] = { "dom1", "dom2", "port1", "port2", NULL };
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwd_list,
- &dom1, &dom2) )
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiii", kwd_list,
+ &dom1, &dom2, &port1, &port2) )
return NULL;
if ( xc_evtchn_bind_interdomain(xc->xc_handle, dom1,
@@ -888,27 +908,6 @@ static PyObject *pyxc_rrobin_global_get(PyObject *self,
return Py_BuildValue("{s:L}", "slice", slice);
}
-static PyObject *pyxc_domain_setname(PyObject *self,
- PyObject *args,
- PyObject *kwds)
-{
- XcObject *xc = (XcObject *)self;
- u32 dom;
- char *name;
-
- static char *kwd_list[] = { "dom", "name", NULL };
-
- if ( !PyArg_ParseTupleAndKeywords(args, kwds, "is", kwd_list,
- &dom, &name) )
- return NULL;
-
- if ( xc_domain_setname(xc->xc_handle, dom, name) != 0 )
- return PyErr_SetFromErrno(xc_error);
-
- Py_INCREF(zero);
- return zero;
-}
-
static PyObject *pyxc_domain_setmaxmem(PyObject *self,
PyObject *args,
PyObject *kwds)
@@ -939,7 +938,6 @@ static PyMethodDef pyxc_methods[] = {
"Create a new domain.\n"
" dom [int, 0]: Domain identifier to use (allocated if zero).\n"
" mem_kb [int, 0]: Memory allocation, in kilobytes.\n"
- " name [str, '(anon)']: Informative textual name.\n\n"
"Returns: [int] new domain identifier; -1 on error.\n" },
{ "domain_pause",
@@ -992,7 +990,6 @@ static PyMethodDef pyxc_methods[] = {
" mem_kb [int]: Memory reservation, in kilobytes\n"
" maxmem_kb [int]: Maximum memory limit, in kilobytes\n"
" cpu_time [long]: CPU time consumed, in nanoseconds\n"
- " name [str]: Identifying name\n"
" shutdown_reason [int]: Numeric code from guest OS, explaining "
"reason why it shut itself down.\n" },
@@ -1004,6 +1001,14 @@ static PyMethodDef pyxc_methods[] = {
" state_file [str]: Name of state file. Must not currently exist.\n"
" progress [int, 1]: Bool - display a running progress indication?\n\n"
"Returns: [int] 0 on success; -1 on error.\n" },
+ { "plan9_build",
+ (PyCFunction)pyxc_plan9_build,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Build a new Plan 9 guest OS.\n"
+ " dom [long]: Identifier of domain to build into.\n"
+ " image [str]: Name of kernel image file. May be gzipped.\n"
+ " cmdline [str, n/a]: Kernel parameters, if any.\n\n"
+ "Returns: [int] 0 on success; -1 on error.\n" },
{ "linux_restore",
(PyCFunction)pyxc_linux_restore,
@@ -1021,14 +1026,17 @@ static PyMethodDef pyxc_methods[] = {
" image [str]: Name of kernel image file. May be gzipped.\n"
" ramdisk [str, n/a]: Name of ramdisk file, if any.\n"
" cmdline [str, n/a]: Kernel parameters, if any.\n\n"
+ " vcpus [int, 1]: Number of Virtual CPUS in domain.\n\n"
"Returns: [int] 0 on success; -1 on error.\n" },
- { "netbsd_build",
- (PyCFunction)pyxc_netbsd_build,
+ { "vmx_build",
+ (PyCFunction)pyxc_vmx_build,
METH_VARARGS | METH_KEYWORDS, "\n"
- "Build a new NetBSD guest OS.\n"
- " dom [int]: Identifier of domain to build into.\n"
+ "Build a new Linux guest OS.\n"
+ " dom [int]: Identifier of domain to build into.\n"
" image [str]: Name of kernel image file. May be gzipped.\n"
+ " memmap [str]: Memory map.\n\n"
+ " ramdisk [str, n/a]: Name of ramdisk file, if any.\n"
" cmdline [str, n/a]: Kernel parameters, if any.\n\n"
"Returns: [int] 0 on success; -1 on error.\n" },
@@ -1071,44 +1079,6 @@ static PyMethodDef pyxc_methods[] = {
" warpl [long]: Warp limit,\n"
},
- { "fbvtsched_global_set",
- (PyCFunction)pyxc_fbvtsched_global_set,
- METH_VARARGS | METH_KEYWORDS, "\n"
- "Set global tuning parameters for Fair Borrowed Virtual Time scheduler.\n"
- " ctx_allow [int]: Minimal guaranteed quantum.\n\n"
- "Returns: [int] 0 on success; -1 on error.\n" },
-
- { "fbvtsched_global_get",
- (PyCFunction)pyxc_fbvtsched_global_get,
- METH_KEYWORDS, "\n"
- "Get global tuning parameters for FBVT scheduler.\n"
- "Returns: [dict]:\n"
- " ctx_allow [int]: context switch allowance\n" },
-
- { "fbvtsched_domain_set",
- (PyCFunction)pyxc_fbvtsched_domain_set,
- METH_VARARGS | METH_KEYWORDS, "\n"
- "Set per-domain tuning parameters for Fair Borrowed Virtual Time scheduler.\n"
- " dom [int]: Identifier of domain to be tuned.\n"
- " mcuadv [int]: Proportional to the inverse of the domain's weight.\n"
- " warp [int]: How far to warp domain's EVT on unblock.\n"
- " warpl [int]: How long the domain can run warped.\n"
- " warpu [int]: How long before the domain can warp again.\n\n"
- "Returns: [int] 0 on success; -1 on error.\n" },
-
- { "fbvtsched_domain_get",
- (PyCFunction)pyxc_fbvtsched_domain_get,
- METH_KEYWORDS, "\n"
- "Get per-domain tuning parameters under the FBVT scheduler.\n"
- " dom [int]: Identifier of domain to be queried.\n"
- "Returns [dict]:\n"
- " domain [int]: Domain ID.\n"
- " mcuadv [long]: MCU Advance.\n"
- " warp [long]: Warp.\n"
- " warpu [long]: Unwarp requirement.\n"
- " warpl [long]: Warp limit,\n"
- },
-
{ "atropos_domain_set",
(PyCFunction)pyxc_atropos_domain_set,
METH_KEYWORDS, "\n"
@@ -1147,6 +1117,13 @@ static PyMethodDef pyxc_methods[] = {
"Returns [dict]:\n"
" slice [long]: Scheduler time slice.\n" },
+ { "evtchn_alloc_unbound",
+ (PyCFunction)pyxc_evtchn_alloc_unbound,
+ METH_VARARGS | METH_KEYWORDS, "\n"
+ "Allocate an unbound local port that will await a remote connection.\n"
+ " dom [int]: Remote domain to accept connections from.\n\n"
+ "Returns: [int] Unbound event-channel port.\n" },
+
{ "evtchn_bind_interdomain",
(PyCFunction)pyxc_evtchn_bind_interdomain,
METH_VARARGS | METH_KEYWORDS, "\n"
@@ -1167,7 +1144,7 @@ static PyMethodDef pyxc_methods[] = {
{ "evtchn_close",
(PyCFunction)pyxc_evtchn_close,
METH_VARARGS | METH_KEYWORDS, "\n"
- "Close an event channel.\n"
+ "Close an event channel. If interdomain, sets remote end to 'unbound'.\n"
" dom [int, SELF]: Dom-id of one endpoint of the channel.\n"
" port [int]: Port-id of one endpoint of the channel.\n\n"
"Returns: [int] 0 on success; -1 on error.\n" },
@@ -1227,14 +1204,6 @@ static PyMethodDef pyxc_methods[] = {
" op [int, 0]: operation\n\n"
"Returns: [int] 0 on success; -1 on error.\n" },
- { "domain_setname",
- (PyCFunction)pyxc_domain_setname,
- METH_VARARGS | METH_KEYWORDS, "\n"
- "Set domain informative textual name\n"
- " dom [int]: Identifier of domain.\n"
- " name [str]: Text string.\n\n"
- "Returns: [int] 0 on success; -1 on error.\n" },
-
{ "domain_setmaxmem",
(PyCFunction)pyxc_domain_setmaxmem,
METH_VARARGS | METH_KEYWORDS, "\n"
@@ -1317,4 +1286,8 @@ PyMODINIT_FUNC initxc(void)
PyDict_SetItemString(d, "error", xc_error);
zero = PyInt_FromLong(0);
+
+ /* KAF: This ensures that we get debug output in a timely manner. */
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
}
diff --git a/tools/python/xen/lowlevel/xu/xu.c b/tools/python/xen/lowlevel/xu/xu.c
index 0a118b7f0f..12f27d63db 100644
--- a/tools/python/xen/lowlevel/xu/xu.c
+++ b/tools/python/xen/lowlevel/xu/xu.c
@@ -15,6 +15,7 @@
#include <sys/socket.h>
#include <sys/mman.h>
#include <sys/poll.h>
+#include <sys/sysmacros.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
@@ -22,10 +23,9 @@
#include <signal.h>
#include <xc.h>
-#include <hypervisor-if.h>
-#include <io/domain_controller.h>
-
-#include <asm-xen/proc_cmd.h>
+#include <xen/xen.h>
+#include <xen/io/domain_controller.h>
+#include <xen/linux/privcmd.h>
#define XENPKG "xen.lowlevel.xu"
@@ -37,7 +37,7 @@
/* NB. The following should be kept in sync with the kernel's evtchn driver. */
#define EVTCHN_DEV_NAME "/dev/xen/evtchn"
#define EVTCHN_DEV_MAJOR 10
-#define EVTCHN_DEV_MINOR 200
+#define EVTCHN_DEV_MINOR 201
/* /dev/xen/evtchn ioctls: */
/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
#define EVTCHN_RESET _IO('E', 1)
@@ -49,7 +49,204 @@
/* Size of a machine page frame. */
#define PAGE_SIZE 4096
+#if defined(__i386__)
+#define rmb() __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define wmb() __asm__ __volatile__ ( "" : : : "memory" )
+#else
+#error "Define barriers"
+#endif
+
+
+/* Set the close-on-exec flag on a file descriptor. Doesn't currently bother
+ * to check for errors. */
+/*
+static void set_cloexec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD, 0);
+
+ if ( flags < 0 )
+ return;
+ flags |= FD_CLOEXEC;
+ fcntl(fd, F_SETFD, flags);
+}
+*/
+/*
+ * *********************** XCS INTERFACE ***********************
+ */
+
+#include <arpa/inet.h>
+#include <xcs_proto.h>
+
+static int xcs_ctrl_fd = -1; /* control connection to the xcs server. */
+static int xcs_data_fd = -1; /* data connection to the xcs server. */
+static u32 xcs_session_id = 0;
+
+static int xcs_ctrl_send(xcs_msg_t *msg);
+static int xcs_ctrl_read(xcs_msg_t *msg);
+static int xcs_data_send(xcs_msg_t *msg);
+static int xcs_data_read(xcs_msg_t *msg);
+
+static int xcs_connect(char *ip, short port)
+{
+ struct sockaddr_in addr;
+ int ret, flags;
+ xcs_msg_t msg;
+
+ if (xcs_data_fd != -1) /* already connected */
+ return 0;
+
+ xcs_ctrl_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (xcs_ctrl_fd < 0)
+ {
+ printf("error creating xcs socket!\n");
+ goto fail;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = inet_addr(ip);
+ memset(&(addr.sin_zero), '\0', 8);
+
+ ret = connect(xcs_ctrl_fd, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr));
+ if (ret < 0)
+ {
+ printf("error connecting to xcs(ctrl)! (%d)\n", errno);
+ goto ctrl_fd_fail;
+ }
+
+ //set_cloexec(xcs_ctrl_fd);
+
+ msg.type = XCS_CONNECT_CTRL;
+ msg.u.connect.session_id = xcs_session_id;
+ xcs_ctrl_send(&msg);
+ xcs_ctrl_read(&msg); /* TODO: timeout + error! */
+
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("error connecting xcs control channel!\n");
+ goto ctrl_fd_fail;
+ }
+ xcs_session_id = msg.u.connect.session_id;
+
+ /* now the data connection. */
+ xcs_data_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (xcs_data_fd < 0)
+ {
+ printf("error creating xcs data socket!\n");
+ goto ctrl_fd_fail;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = inet_addr(ip);
+ memset(&(addr.sin_zero), '\0', 8);
+
+ ret = connect(xcs_data_fd, (struct sockaddr *)&addr,
+ sizeof(struct sockaddr));
+ if (ret < 0)
+ {
+ printf("error connecting to xcs(data)! (%d)\n", errno);
+ goto data_fd_fail;
+ }
+
+ //set_cloexec(xcs_data_fd);
+ msg.type = XCS_CONNECT_DATA;
+ msg.u.connect.session_id = xcs_session_id;
+ xcs_data_send(&msg);
+ xcs_data_read(&msg); /* TODO: timeout + error! */
+
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("error connecting xcs control channel!\n");
+ goto ctrl_fd_fail;
+ }
+
+ if ( ((flags = fcntl(xcs_data_fd, F_GETFL, 0)) < 0) ||
+ (fcntl(xcs_data_fd, F_SETFL, flags | O_NONBLOCK) < 0) )
+ {
+ printf("Unable to set non-blocking status on data socket.");
+ goto data_fd_fail;
+ }
+
+ return 0;
+
+data_fd_fail:
+ close(xcs_data_fd);
+ xcs_data_fd = -1;
+
+ctrl_fd_fail:
+ close(xcs_ctrl_fd);
+ xcs_ctrl_fd = -1;
+
+fail:
+ return -1;
+
+}
+
+static void xcs_disconnect(void)
+{
+ close(xcs_data_fd);
+ xcs_data_fd = -1;
+ close(xcs_ctrl_fd);
+ xcs_ctrl_fd = -1;
+}
+
+static int xcs_ctrl_read(xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = read(xcs_ctrl_fd, msg, sizeof(xcs_msg_t));
+ return ret;
+}
+
+static int xcs_ctrl_send(xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = send(xcs_ctrl_fd, msg, sizeof(xcs_msg_t), 0);
+ return ret;
+}
+
+static int xcs_data_read(xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = read(xcs_data_fd, msg, sizeof(xcs_msg_t));
+ return ret;
+}
+
+static int xcs_data_send(xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = send(xcs_data_fd, msg, sizeof(xcs_msg_t), 0);
+ return ret;
+}
+
+
+typedef struct kme_st {
+ xcs_msg_t msg;
+ struct kme_st *next;
+} xcs_msg_ent_t;
+
+
+#define XCS_RING_SIZE 64
+static xcs_msg_ent_t *req_ring[64];
+static unsigned req_prod = 0;
+static unsigned req_cons = 0;
+
+static xcs_msg_ent_t *rsp_ring[64];
+static unsigned rsp_prod = 0;
+static unsigned rsp_cons = 0;
+
+#define REQ_RING_ENT(_idx) (req_ring[(_idx) % XCS_RING_SIZE])
+#define RSP_RING_ENT(_idx) (rsp_ring[(_idx) % XCS_RING_SIZE])
+#define REQ_RING_FULL ( req_prod - req_cons == XCS_RING_SIZE )
+#define RSP_RING_FULL ( rsp_prod - rsp_cons == XCS_RING_SIZE )
+#define REQ_RING_EMPTY ( req_prod == req_cons )
+#define RSP_RING_EMPTY ( rsp_prod == rsp_cons )
/*
* *********************** NOTIFIER ***********************
*/
@@ -61,81 +258,136 @@ typedef struct {
static PyObject *xu_notifier_read(PyObject *self, PyObject *args)
{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- u16 v;
- int bytes;
+ xcs_msg_ent_t *ent;
+ int ret;
if ( !PyArg_ParseTuple(args, "") )
return NULL;
-
- while ( (bytes = read(xun->evtchn_fd, &v, sizeof(v))) == -1 )
+
+ while ((!REQ_RING_FULL) && (!RSP_RING_FULL))
{
- if ( errno == EINTR )
+ ent = (xcs_msg_ent_t *)malloc(sizeof(xcs_msg_ent_t));
+ ret = xcs_data_read(&ent->msg);
+
+ if (ret == -1)
+ {
+ free(ent);
+ if ( errno == EINTR )
+ continue;
+ if ( errno == EAGAIN )
+ break;
+ return PyErr_SetFromErrno(PyExc_IOError);
+ }
+
+ switch (ent->msg.type)
+ {
+ case XCS_REQUEST:
+ REQ_RING_ENT(req_prod) = ent;
+ req_prod++;
continue;
- if ( errno == EAGAIN )
- goto none;
- return PyErr_SetFromErrno(PyExc_IOError);
+
+ case XCS_RESPONSE:
+ RSP_RING_ENT(rsp_prod) = ent;
+ rsp_prod++;
+ continue;
+
+ case XCS_VIRQ:
+ ret = ent->msg.u.control.local_port;
+ free(ent);
+ return PyInt_FromLong(ret);
+
+ default:
+ /*printf("Throwing away xcs msg type: %u\n", ent->msg.type);*/
+ free(ent);
+ }
+ }
+
+ if (!REQ_RING_EMPTY)
+ {
+ return PyInt_FromLong(REQ_RING_ENT(req_cons)->msg.u.control.local_port);
+ }
+
+ if (!RSP_RING_EMPTY)
+ {
+ return PyInt_FromLong(RSP_RING_ENT(rsp_cons)->msg.u.control.local_port);
}
- if ( bytes == sizeof(v) )
- return PyInt_FromLong(v);
-
- none:
Py_INCREF(Py_None);
return Py_None;
}
+/* this is now a NOOP */
static PyObject *xu_notifier_unmask(PyObject *self, PyObject *args)
{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- u16 v;
- int idx;
-
- if ( !PyArg_ParseTuple(args, "i", &idx) )
- return NULL;
-
- v = (u16)idx;
-
- (void)write(xun->evtchn_fd, &v, sizeof(v));
-
Py_INCREF(Py_None);
return Py_None;
}
+/* this is now a NOOP */
static PyObject *xu_notifier_bind(PyObject *self, PyObject *args)
{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- int idx;
-
- if ( !PyArg_ParseTuple(args, "i", &idx) )
- return NULL;
-
- if ( ioctl(xun->evtchn_fd, EVTCHN_BIND, idx) != 0 )
- return PyErr_SetFromErrno(PyExc_IOError);
-
Py_INCREF(Py_None);
return Py_None;
}
-static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args)
+static PyObject *xu_notifier_bind_virq(PyObject *self,
+ PyObject *args, PyObject *kwds)
{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- int idx;
+ int virq;
+ xcs_msg_t kmsg;
- if ( !PyArg_ParseTuple(args, "i", &idx) )
+ static char *kwd_list[] = { "virq", NULL };
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &virq) )
return NULL;
+
+ kmsg.type = XCS_VIRQ_BIND;
+ kmsg.u.virq.virq = virq;
+ xcs_ctrl_send(&kmsg);
+ xcs_ctrl_read(&kmsg);
+
+ if ( kmsg.result != XCS_RSLT_OK )
+ {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return PyInt_FromLong(kmsg.u.virq.port);
+}
+
+static PyObject *xu_notifier_virq_send(PyObject *self,
+ PyObject *args, PyObject *kwds)
+{
+ int port;
+ xcs_msg_t kmsg;
- if ( ioctl(xun->evtchn_fd, EVTCHN_UNBIND, idx) != 0 )
- return PyErr_SetFromErrno(PyExc_IOError);
+ static char *kwd_list[] = { "port", NULL };
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &port) )
+ return NULL;
+
+ kmsg.type = XCS_VIRQ;
+ kmsg.u.control.local_port = port;
+ xcs_ctrl_send(&kmsg);
+ xcs_ctrl_read(&kmsg);
+
+ if ( kmsg.result != XCS_RSLT_OK )
+ {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ return PyInt_FromLong(kmsg.u.virq.port);
+}
+/* this is now a NOOP */
+static PyObject *xu_notifier_unbind(PyObject *self, PyObject *args)
+{
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *xu_notifier_fileno(PyObject *self, PyObject *args)
{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- return PyInt_FromLong(xun->evtchn_fd);
+ return PyInt_FromLong(xcs_data_fd);
}
static PyMethodDef xu_notifier_methods[] = {
@@ -158,6 +410,18 @@ static PyMethodDef xu_notifier_methods[] = {
(PyCFunction)xu_notifier_unbind,
METH_VARARGS,
"No longer get notifications for a @port.\n" },
+
+ { "bind_virq",
+ (PyCFunction)xu_notifier_bind_virq,
+ METH_VARARGS | METH_KEYWORDS,
+ "Get notifications for a virq.\n"
+ " virq [int]: VIRQ to bind.\n\n" },
+
+ { "virq_send",
+ (PyCFunction)xu_notifier_virq_send,
+ METH_VARARGS | METH_KEYWORDS,
+ "Fire a virq notification.\n"
+ " port [int]: port that VIRQ is bound to.\n\n" },
{ "fileno",
(PyCFunction)xu_notifier_fileno,
@@ -169,27 +433,22 @@ static PyMethodDef xu_notifier_methods[] = {
staticforward PyTypeObject xu_notifier_type;
+/* connect to xcs if we aren't already, and return a dummy object. */
static PyObject *xu_notifier_new(PyObject *self, PyObject *args)
{
xu_notifier_object *xun;
+ int i;
if ( !PyArg_ParseTuple(args, "") )
return NULL;
xun = PyObject_New(xu_notifier_object, &xu_notifier_type);
- reopen:
- xun->evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
- if ( xun->evtchn_fd == -1 )
- {
- if ( (errno == ENOENT) &&
- ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) &&
- (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600,
- (EVTCHN_DEV_MAJOR << 8) | EVTCHN_DEV_MINOR) == 0) )
- goto reopen;
- PyObject_Del((PyObject *)xun);
- return PyErr_SetFromErrno(PyExc_IOError);
- }
+ for (i = 0; i < XCS_RING_SIZE; i++)
+ REQ_RING_ENT(i) = RSP_RING_ENT(i) = NULL;
+
+ (void)xcs_connect("127.0.0.1", XCS_TCP_PORT);
+
return (PyObject *)xun;
}
@@ -201,8 +460,7 @@ static PyObject *xu_notifier_getattr(PyObject *obj, char *name)
static void xu_notifier_dealloc(PyObject *self)
{
- xu_notifier_object *xun = (xu_notifier_object *)self;
- (void)close(xun->evtchn_fd);
+ xcs_disconnect();
PyObject_Del(self);
}
@@ -259,6 +517,24 @@ static PyTypeObject xu_notifier_type = {
PyDict_SetItemString(dict, #_field, obj); \
} while ( 0 )
+#define PSTR2CHAR(_struct, _field) \
+ do { \
+ PyObject *obj; \
+ if ( (obj = PyDict_GetItemString(payload, #_field)) != NULL ) \
+ { \
+ if ( PyString_Check(obj) ) \
+ { \
+ char *buffer = PyString_AsString(obj); \
+ \
+ strcpy(((_struct *)&xum->msg.msg[0])->_field, \
+ buffer); \
+ /* Should complain about length - think later */ \
+ dict_items_parsed++; \
+ } \
+ } \
+ xum->msg.length = sizeof(_struct); \
+ } while ( 0 )
+
typedef struct {
PyObject_HEAD;
control_msg_t msg;
@@ -303,11 +579,11 @@ static PyObject *xu_message_set_response_fields(PyObject *self, PyObject *args)
switch ( TYPE(xum->msg.type, xum->msg.subtype) )
{
- case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED):
- P2C(blkif_fe_driver_status_changed_t, nr_interfaces, u32);
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS):
+ P2C(blkif_fe_driver_status_t, max_handle, u32);
break;
- case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
- P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32);
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS):
+ P2C(netif_fe_driver_status_t, max_handle, u32);
break;
}
@@ -331,13 +607,13 @@ static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
switch ( TYPE(xum->msg.type, xum->msg.subtype) )
{
- case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED):
- C2P(blkif_fe_interface_status_changed_t, handle, Int, Long);
- C2P(blkif_fe_interface_status_changed_t, status, Int, Long);
- C2P(blkif_fe_interface_status_changed_t, evtchn, Int, Long);
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS):
+ C2P(blkif_fe_interface_status_t, handle, Int, Long);
+ C2P(blkif_fe_interface_status_t, status, Int, Long);
+ C2P(blkif_fe_interface_status_t, evtchn, Int, Long);
return dict;
- case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED):
- C2P(blkif_fe_driver_status_changed_t, status, Int, Long);
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS):
+ C2P(blkif_fe_driver_status_t, status, Int, Long);
return dict;
case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT):
C2P(blkif_fe_interface_connect_t, handle, Int, Long);
@@ -398,17 +674,23 @@ static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
C2P(blkif_be_vbd_shrink_t, vdevice, Int, Long);
C2P(blkif_be_vbd_shrink_t, status, Int, Long);
return dict;
- case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED):
- C2P(blkif_be_driver_status_changed_t, status, Int, Long);
+ case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DRIVER_STATUS):
+ C2P(blkif_be_driver_status_t, status, Int, Long);
return dict;
- case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED):
- C2P(netif_fe_interface_status_changed_t, handle, Int, Long);
- C2P(netif_fe_interface_status_changed_t, status, Int, Long);
- C2P(netif_fe_interface_status_changed_t, evtchn, Int, Long);
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS):
+ C2P(netif_fe_interface_status_t, handle, Int, Long);
+ C2P(netif_fe_interface_status_t, status, Int, Long);
+ C2P(netif_fe_interface_status_t, evtchn, Int, Long);
+ C2P(netif_fe_interface_status_t, mac[0], Int, Long);
+ C2P(netif_fe_interface_status_t, mac[1], Int, Long);
+ C2P(netif_fe_interface_status_t, mac[2], Int, Long);
+ C2P(netif_fe_interface_status_t, mac[3], Int, Long);
+ C2P(netif_fe_interface_status_t, mac[4], Int, Long);
+ C2P(netif_fe_interface_status_t, mac[5], Int, Long);
return dict;
- case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
- C2P(netif_fe_driver_status_changed_t, status, Int, Long);
- C2P(netif_fe_driver_status_changed_t, nr_interfaces, Int, Long);
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS):
+ C2P(netif_fe_driver_status_t, status, Int, Long);
+ C2P(netif_fe_driver_status_t, max_handle, Int, Long);
return dict;
case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT):
C2P(netif_fe_interface_connect_t, handle, Int, Long);
@@ -441,8 +723,58 @@ static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
C2P(netif_be_disconnect_t, netif_handle, Int, Long);
C2P(netif_be_disconnect_t, status, Int, Long);
return dict;
- case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED):
- C2P(netif_be_driver_status_changed_t, status, Int, Long);
+ case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS):
+ C2P(netif_be_driver_status_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED):
+ C2P(usbif_fe_interface_status_changed_t, status, Int, Long);
+ C2P(usbif_fe_interface_status_changed_t, evtchn, Int, Long);
+ C2P(usbif_fe_interface_status_changed_t, domid, Int, Long);
+ C2P(usbif_fe_interface_status_changed_t, bandwidth, Int, Long);
+ C2P(usbif_fe_interface_status_changed_t, num_ports, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED):
+ C2P(usbif_fe_driver_status_changed_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT):
+ C2P(usbif_fe_interface_connect_t, shmem_frame, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT):
+ return dict;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE):
+ C2P(usbif_be_create_t, domid, Int, Long);
+ C2P(usbif_be_create_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY):
+ C2P(usbif_be_destroy_t, domid, Int, Long);
+ C2P(usbif_be_destroy_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT):
+ C2P(usbif_be_connect_t, domid, Int, Long);
+ C2P(usbif_be_connect_t, shmem_frame, Int, Long);
+ C2P(usbif_be_connect_t, evtchn, Int, Long);
+ C2P(usbif_be_connect_t, bandwidth, Int, Long);
+ C2P(usbif_be_connect_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT):
+ C2P(usbif_be_disconnect_t, domid, Int, Long);
+ C2P(usbif_be_disconnect_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DRIVER_STATUS_CHANGED):
+ C2P(usbif_be_driver_status_changed_t, status, Int, Long);
+ return dict;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT):
+ C2P(usbif_be_claim_port_t, domid, Int, Long);
+ C2P(usbif_be_claim_port_t, usbif_port, Int, Long);
+ C2P(usbif_be_claim_port_t, status, Int, Long);
+ C2P(usbif_be_claim_port_t, path, String, String);
+ return dict;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT):
+ C2P(usbif_be_release_port_t, path, String, String);
+ return dict;
+ case TYPE(CMSG_MEM_REQUEST, CMSG_MEM_REQUEST_SET):
+ C2P(mem_request_t, target, Int, Long);
+ C2P(mem_request_t, status, Int, Long);
return dict;
}
@@ -516,10 +848,11 @@ static PyObject *xu_message_new(PyObject *self, PyObject *args)
switch ( TYPE(type, subtype) )
{
- case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED):
- P2C(blkif_fe_interface_status_changed_t, handle, u32);
- P2C(blkif_fe_interface_status_changed_t, status, u32);
- P2C(blkif_fe_interface_status_changed_t, evtchn, u16);
+ case TYPE(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS):
+ P2C(blkif_fe_interface_status_t, handle, u32);
+ P2C(blkif_fe_interface_status_t, status, u32);
+ P2C(blkif_fe_interface_status_t, evtchn, u16);
+ P2C(blkif_fe_interface_status_t, domid, u16);
break;
case TYPE(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE):
P2C(blkif_be_create_t, domid, u32);
@@ -563,16 +896,17 @@ static PyObject *xu_message_new(PyObject *self, PyObject *args)
P2C(blkif_be_vbd_shrink_t, blkif_handle, u32);
P2C(blkif_be_vbd_shrink_t, vdevice, blkif_vdev_t);
break;
- case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED):
- P2C(netif_fe_interface_status_changed_t, handle, u32);
- P2C(netif_fe_interface_status_changed_t, status, u32);
- P2C(netif_fe_interface_status_changed_t, evtchn, u16);
- P2C(netif_fe_interface_status_changed_t, mac[0], u8);
- P2C(netif_fe_interface_status_changed_t, mac[1], u8);
- P2C(netif_fe_interface_status_changed_t, mac[2], u8);
- P2C(netif_fe_interface_status_changed_t, mac[3], u8);
- P2C(netif_fe_interface_status_changed_t, mac[4], u8);
- P2C(netif_fe_interface_status_changed_t, mac[5], u8);
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS):
+ P2C(netif_fe_interface_status_t, handle, u32);
+ P2C(netif_fe_interface_status_t, status, u32);
+ P2C(netif_fe_interface_status_t, evtchn, u16);
+ P2C(netif_fe_interface_status_t, domid, u16);
+ P2C(netif_fe_interface_status_t, mac[0], u8);
+ P2C(netif_fe_interface_status_t, mac[1], u8);
+ P2C(netif_fe_interface_status_t, mac[2], u8);
+ P2C(netif_fe_interface_status_t, mac[3], u8);
+ P2C(netif_fe_interface_status_t, mac[4], u8);
+ P2C(netif_fe_interface_status_t, mac[5], u8);
break;
case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE):
P2C(netif_be_create_t, domid, u32);
@@ -583,6 +917,12 @@ static PyObject *xu_message_new(PyObject *self, PyObject *args)
P2C(netif_be_create_t, mac[3], u8);
P2C(netif_be_create_t, mac[4], u8);
P2C(netif_be_create_t, mac[5], u8);
+ P2C(netif_be_create_t, be_mac[0], u8);
+ P2C(netif_be_create_t, be_mac[1], u8);
+ P2C(netif_be_create_t, be_mac[2], u8);
+ P2C(netif_be_create_t, be_mac[3], u8);
+ P2C(netif_be_create_t, be_mac[4], u8);
+ P2C(netif_be_create_t, be_mac[5], u8);
break;
case TYPE(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY):
P2C(netif_be_destroy_t, domid, u32);
@@ -599,9 +939,60 @@ static PyObject *xu_message_new(PyObject *self, PyObject *args)
P2C(netif_be_disconnect_t, domid, u32);
P2C(netif_be_disconnect_t, netif_handle, u32);
break;
- case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
- P2C(netif_fe_driver_status_changed_t, status, u32);
- P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32);
+ case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS):
+ P2C(netif_fe_driver_status_t, status, u32);
+ P2C(netif_fe_driver_status_t, max_handle, u32);
+ break;
+ case TYPE(CMSG_MEM_REQUEST, CMSG_MEM_REQUEST_SET):
+ P2C(mem_request_t, target, u32);
+ P2C(mem_request_t, status, u32);
+ break;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED):
+ P2C(usbif_fe_interface_status_changed_t, status, u32);
+ P2C(usbif_fe_interface_status_changed_t, evtchn, u16);
+ P2C(usbif_fe_interface_status_changed_t, domid, domid_t);
+ P2C(usbif_fe_interface_status_changed_t, bandwidth, u32);
+ P2C(usbif_fe_interface_status_changed_t, num_ports, u32);
+ break;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED):
+ P2C(usbif_fe_driver_status_changed_t, status, u32);
+ break;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT):
+ P2C(usbif_fe_interface_connect_t, shmem_frame, memory_t);
+ break;
+ case TYPE(CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT):
+ break;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE):
+ P2C(usbif_be_create_t, domid, domid_t);
+ P2C(usbif_be_create_t, status, u32);
+ break;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY):
+ P2C(usbif_be_destroy_t, domid, domid_t);
+ P2C(usbif_be_destroy_t, status, u32);
+ break;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT):
+ P2C(usbif_be_connect_t, domid, domid_t);
+ P2C(usbif_be_connect_t, shmem_frame, memory_t);
+ P2C(usbif_be_connect_t, evtchn, u32);
+ P2C(usbif_be_connect_t, bandwidth, u32);
+ P2C(usbif_be_connect_t, status, u32);
+ break;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT):
+ P2C(usbif_be_disconnect_t, domid, domid_t);
+ P2C(usbif_be_disconnect_t, status, u32);
+ break;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_DRIVER_STATUS_CHANGED):
+ P2C(usbif_be_driver_status_changed_t, status, u32);
+ break;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT):
+ P2C(usbif_be_claim_port_t, domid, domid_t);
+ P2C(usbif_be_claim_port_t, usbif_port, u32);
+ P2C(usbif_be_claim_port_t, status, u32);
+ PSTR2CHAR(usbif_be_claim_port_t, path);
+ printf("dict items parsed = %d", dict_items_parsed);
+ break;
+ case TYPE(CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT):
+ PSTR2CHAR(usbif_be_release_port_t, path);
break;
}
@@ -652,42 +1043,20 @@ static PyTypeObject xu_message_type = {
* *********************** PORT ***********************
*/
-static control_if_t *map_control_interface(int fd, unsigned long pfn)
-{
- char *vaddr = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
- MAP_SHARED, fd, pfn * PAGE_SIZE);
- if ( vaddr == MAP_FAILED )
- return NULL;
- return (control_if_t *)(vaddr + 2048);
-}
-static void unmap_control_interface(int fd, control_if_t *c)
-{
- char *vaddr = (char *)c - 2048;
- (void)munmap(vaddr, PAGE_SIZE);
-}
-
typedef struct xu_port_object {
PyObject_HEAD;
- int mem_fd;
int xc_handle;
+ int connected;
u32 remote_dom;
int local_port, remote_port;
- control_if_t *interface;
- CONTROL_RING_IDX tx_req_cons, tx_resp_prod;
- CONTROL_RING_IDX rx_req_prod, rx_resp_cons;
+ struct xu_port_object *fix_next;
} xu_port_object;
static PyObject *port_error;
+/* now a NOOP */
static PyObject *xu_port_notify(PyObject *self, PyObject *args)
{
- xu_port_object *xup = (xu_port_object *)self;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- (void)xc_evtchn_send(xup->xc_handle, xup->local_port);
-
Py_INCREF(Py_None);
return Py_None;
}
@@ -696,36 +1065,47 @@ static PyObject *xu_port_read_request(PyObject *self, PyObject *args)
{
xu_port_object *xup = (xu_port_object *)self;
xu_message_object *xum;
- CONTROL_RING_IDX c = xup->tx_req_cons;
- control_if_t *cif = xup->interface;
control_msg_t *cmsg;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( (c == cif->tx_req_prod) ||
- ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
- {
- PyErr_SetString(port_error, "no request to read");
- return NULL;
+ unsigned i;
+ xcs_msg_ent_t *ent = NULL;
+
+ for ( i = req_cons; (i != req_prod); i++ ) {
+ ent = REQ_RING_ENT(i);
+ if (ent == NULL)
+ continue;
+ if (ent->msg.u.control.remote_dom == xup->remote_dom)
+ break;
}
+
+ if ((ent == NULL) ||
+ (ent->msg.u.control.remote_dom != xup->remote_dom))
+ goto none;
- cmsg = &cif->tx_ring[MASK_CONTROL_IDX(c)];
+ cmsg = &ent->msg.u.control.msg;
xum = PyObject_New(xu_message_object, &xu_message_type);
memcpy(&xum->msg, cmsg, sizeof(*cmsg));
if ( xum->msg.length > sizeof(xum->msg.msg) )
xum->msg.length = sizeof(xum->msg.msg);
- xup->tx_req_cons++;
+ free(ent);
+
+ /* remove the entry from the ring and advance the consumer if possible */
+ REQ_RING_ENT(i) = NULL;
+ while ( (REQ_RING_ENT(req_cons) == NULL) && (!REQ_RING_EMPTY) )
+ req_cons++;
+
return (PyObject *)xum;
+
+none:
+ Py_INCREF(Py_None);
+ return Py_None;
+
}
static PyObject *xu_port_write_request(PyObject *self, PyObject *args)
{
xu_port_object *xup = (xu_port_object *)self;
xu_message_object *xum;
- CONTROL_RING_IDX p = xup->rx_req_prod;
- control_if_t *cif = xup->interface;
- control_msg_t *cmsg;
+ xcs_msg_t kmsg;
if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
return NULL;
@@ -736,17 +1116,11 @@ static PyObject *xu_port_write_request(PyObject *self, PyObject *args)
return NULL;
}
- if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
- {
- PyErr_SetString(port_error, "no space to write request");
- return NULL;
- }
-
- cmsg = &cif->rx_ring[MASK_CONTROL_IDX(p)];
- memcpy(cmsg, &xum->msg, sizeof(*cmsg));
-
- xup->rx_req_prod = cif->rx_req_prod = p + 1;
-
+ kmsg.type = XCS_REQUEST;
+ kmsg.u.control.remote_dom = xup->remote_dom;
+ memcpy(&kmsg.u.control.msg, &xum->msg, sizeof(control_msg_t));
+ xcs_data_send(&kmsg);
+
Py_INCREF(Py_None);
return Py_None;
}
@@ -755,35 +1129,47 @@ static PyObject *xu_port_read_response(PyObject *self, PyObject *args)
{
xu_port_object *xup = (xu_port_object *)self;
xu_message_object *xum;
- CONTROL_RING_IDX c = xup->rx_resp_cons;
- control_if_t *cif = xup->interface;
control_msg_t *cmsg;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
-
- if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
- {
- PyErr_SetString(port_error, "no response to read");
- return NULL;
+ unsigned i;
+ xcs_msg_ent_t *ent = NULL;
+
+ for ( i = rsp_cons; (i != rsp_prod); i++ ) {
+ ent = RSP_RING_ENT(i);
+ if (ent == NULL)
+ continue;
+ if (ent->msg.u.control.remote_dom == xup->remote_dom)
+ break;
}
+
+ if ((ent == NULL) ||
+ (ent->msg.u.control.remote_dom != xup->remote_dom))
+ goto none;
- cmsg = &cif->rx_ring[MASK_CONTROL_IDX(c)];
+ cmsg = &ent->msg.u.control.msg;
xum = PyObject_New(xu_message_object, &xu_message_type);
memcpy(&xum->msg, cmsg, sizeof(*cmsg));
if ( xum->msg.length > sizeof(xum->msg.msg) )
xum->msg.length = sizeof(xum->msg.msg);
- xup->rx_resp_cons++;
+ free(ent);
+
+ /* remove the entry from the ring and advance the consumer if possible */
+ RSP_RING_ENT(i) = NULL;
+ while ( (RSP_RING_ENT(rsp_cons) == NULL) && (!RSP_RING_EMPTY) )
+ rsp_cons++;
+
return (PyObject *)xum;
+
+none:
+ Py_INCREF(Py_None);
+ return Py_None;
+
}
static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
{
xu_port_object *xup = (xu_port_object *)self;
xu_message_object *xum;
- CONTROL_RING_IDX p = xup->tx_resp_prod;
- control_if_t *cif = xup->interface;
- control_msg_t *cmsg;
+ xcs_msg_t kmsg;
if ( !PyArg_ParseTuple(args, "O", (PyObject **)&xum) )
return NULL;
@@ -794,16 +1180,10 @@ static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
return NULL;
}
- if ( p == xup->tx_req_cons )
- {
- PyErr_SetString(port_error, "no space to write response");
- return NULL;
- }
-
- cmsg = &cif->tx_ring[MASK_CONTROL_IDX(p)];
- memcpy(cmsg, &xum->msg, sizeof(*cmsg));
-
- xup->tx_resp_prod = cif->tx_resp_prod = p + 1;
+ kmsg.type = XCS_RESPONSE;
+ kmsg.u.control.remote_dom = xup->remote_dom;
+ memcpy(&kmsg.u.control.msg, &xum->msg, sizeof(control_msg_t));
+ xcs_data_send(&kmsg);
Py_INCREF(Py_None);
return Py_None;
@@ -811,142 +1191,131 @@ static PyObject *xu_port_write_response(PyObject *self, PyObject *args)
static PyObject *xu_port_request_to_read(PyObject *self, PyObject *args)
{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX c = xup->tx_req_cons;
- control_if_t *cif = xup->interface;
-
+ xu_port_object *xup = (xu_port_object *)self;
+ xcs_msg_ent_t *ent;
+ int found = 0;
+ unsigned i;
+
if ( !PyArg_ParseTuple(args, "") )
return NULL;
- if ( (c == cif->tx_req_prod) ||
- ((c - xup->tx_resp_prod) == CONTROL_RING_SIZE) )
- return PyInt_FromLong(0);
-
- return PyInt_FromLong(1);
+ for ( i = req_cons; (i != req_prod); i++ ) {
+ ent = REQ_RING_ENT(i);
+ if (ent == NULL)
+ continue;
+ if (ent->msg.u.control.remote_dom == xup->remote_dom) {
+ found = 1;
+ break;
+ }
+ }
+
+ return PyInt_FromLong(found);
}
static PyObject *xu_port_space_to_write_request(PyObject *self, PyObject *args)
{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX p = xup->rx_req_prod;
-
if ( !PyArg_ParseTuple(args, "") )
return NULL;
- if ( ((p - xup->rx_resp_cons) == CONTROL_RING_SIZE) )
- return PyInt_FromLong(0);
-
return PyInt_FromLong(1);
}
static PyObject *xu_port_response_to_read(PyObject *self, PyObject *args)
{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX c = xup->rx_resp_cons;
- control_if_t *cif = xup->interface;
-
+ xu_port_object *xup = (xu_port_object *)self;
+ xcs_msg_ent_t *ent;
+ int found = 0;
+ unsigned i;
+
if ( !PyArg_ParseTuple(args, "") )
return NULL;
- if ( (c == cif->rx_resp_prod) || (c == xup->rx_req_prod) )
- return PyInt_FromLong(0);
-
- return PyInt_FromLong(1);
+ for ( i = rsp_cons; (i != rsp_prod); i++ ) {
+ ent = RSP_RING_ENT(i);
+ if (ent == NULL)
+ continue;
+ if (ent->msg.u.control.remote_dom == xup->remote_dom) {
+ found = 1;
+ break;
+ }
+ }
+
+ return PyInt_FromLong(found);
}
static PyObject *xu_port_space_to_write_response(
PyObject *self, PyObject *args)
{
- xu_port_object *xup = (xu_port_object *)self;
- CONTROL_RING_IDX p = xup->tx_resp_prod;
-
if ( !PyArg_ParseTuple(args, "") )
return NULL;
- if ( p == xup->tx_req_cons )
- return PyInt_FromLong(0);
-
return PyInt_FromLong(1);
}
-static int __xu_port_connect(xu_port_object *xup)
+/* NOOP */
+static PyObject *xu_port_connect(PyObject *self, PyObject *args)
{
- xc_dominfo_t info;
-
- if ( xup->mem_fd != -1 )
- return 0;
-
- if ( (xup->mem_fd = open("/dev/mem", O_RDWR)) == -1 )
- {
- PyErr_SetString(port_error, "Could not open '/dev/mem'");
- return -1;
- }
-
- /* Set the General-Purpose Subject whose page frame will be mapped. */
- (void)ioctl(xup->mem_fd, _IO('M', 1), (unsigned long)xup->remote_dom);
-
- if ( (xc_domain_getinfo(xup->xc_handle, xup->remote_dom, 1, &info) != 1) ||
- (info.domid != xup->remote_dom) )
- {
- PyErr_SetString(port_error, "Failed to obtain domain status");
- (void)close(xup->mem_fd);
- xup->mem_fd = -1;
- return -1;
- }
-
- xup->interface =
- map_control_interface(xup->mem_fd, info.shared_info_frame);
- if ( xup->interface == NULL )
- {
- PyErr_SetString(port_error, "Failed to map domain control interface");
- (void)close(xup->mem_fd);
- xup->mem_fd = -1;
- return -1;
- }
-
- /* Synchronise ring indexes. */
- xup->tx_resp_prod = xup->interface->tx_resp_prod;
- xup->tx_req_cons = xup->interface->tx_resp_prod;
- xup->rx_req_prod = xup->interface->rx_req_prod;
- xup->rx_resp_cons = xup->interface->rx_resp_prod;
-
- return 0;
+ Py_INCREF(Py_None);
+ return Py_None;
}
-static void __xu_port_disconnect(xu_port_object *xup)
+/* NOOP */
+static PyObject *xu_port_disconnect(PyObject *self, PyObject *args)
{
- if ( xup->mem_fd == -1 )
- return;
- unmap_control_interface(xup->mem_fd, xup->interface);
- (void)close(xup->mem_fd);
- xup->mem_fd = -1;
+ Py_INCREF(Py_None);
+ return Py_None;
}
-static PyObject *xu_port_connect(PyObject *self, PyObject *args)
+static PyObject *xu_port_register(PyObject *self, PyObject *args,
+ PyObject *kwds)
{
- xu_port_object *xup = (xu_port_object *)self;
-
- if ( !PyArg_ParseTuple(args, "") )
- return NULL;
+ int type;
+ xcs_msg_t msg;
+ xu_port_object *xup = (xu_port_object *)self;
+ static char *kwd_list[] = { "type", NULL };
- if ( __xu_port_connect(xup) != 0 )
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list,
+ &type) )
return NULL;
-
- Py_INCREF(Py_None);
- return Py_None;
+
+ msg.type = XCS_MSG_BIND;
+ msg.u.bind.port = xup->local_port;
+ msg.u.bind.type = type;
+ xcs_ctrl_send(&msg);
+ xcs_ctrl_read(&msg);
+
+ if (msg.result != XCS_RSLT_OK)
+ {
+ return PyInt_FromLong(0);
+ }
+
+ return PyInt_FromLong(1);
}
-static PyObject *xu_port_disconnect(PyObject *self, PyObject *args)
+static PyObject *xu_port_deregister(PyObject *self, PyObject *args,
+ PyObject *kwds)
{
- xu_port_object *xup = (xu_port_object *)self;
+ int type;
+ xcs_msg_t msg;
+ xu_port_object *xup = (xu_port_object *)self;
+ static char *kwd_list[] = { "type", NULL };
- if ( !PyArg_ParseTuple(args, "") )
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list,
+ &type) )
return NULL;
-
- __xu_port_disconnect(xup);
-
- Py_INCREF(Py_None);
- return Py_None;
+
+ msg.type = XCS_MSG_UNBIND;
+ msg.u.bind.port = xup->local_port;
+ msg.u.bind.type = type;
+ xcs_ctrl_send(&msg);
+ xcs_ctrl_read(&msg);
+
+ if (msg.result != XCS_RSLT_OK)
+ {
+ return PyInt_FromLong(0);
+ }
+
+ return PyInt_FromLong(1);
}
static PyMethodDef xu_port_methods[] = {
@@ -994,6 +1363,16 @@ static PyMethodDef xu_port_methods[] = {
(PyCFunction)xu_port_space_to_write_response,
METH_VARARGS,
"Returns TRUE if there is space to write a response message.\n" },
+
+ { "register",
+ (PyCFunction)xu_port_register,
+ METH_VARARGS | METH_KEYWORDS,
+ "Register to receive a type of message on this channel.\n" },
+
+ { "deregister",
+ (PyCFunction)xu_port_deregister,
+ METH_VARARGS | METH_KEYWORDS,
+ "Stop receiving a type of message on this port.\n" },
{ "connect",
(PyCFunction)xu_port_connect,
@@ -1010,64 +1389,43 @@ static PyMethodDef xu_port_methods[] = {
staticforward PyTypeObject xu_port_type;
-static PyObject *xu_port_new(PyObject *self, PyObject *args)
+static PyObject *xu_port_new(PyObject *self, PyObject *args, PyObject *kwds)
{
xu_port_object *xup;
u32 dom;
- int port1, port2;
+ int port1 = 0, port2 = 0;
+ xcs_msg_t kmsg;
- if ( !PyArg_ParseTuple(args, "i", &dom) )
+ static char *kwd_list[] = { "dom", "local_port", "remote_port", NULL };
+
+ if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|ii", kwd_list,
+ &dom, &port1, &port2) )
return NULL;
xup = PyObject_New(xu_port_object, &xu_port_type);
+ xup->connected = 0;
xup->remote_dom = dom;
- xup->mem_fd = -1; /* currently disconnected */
-
- if ( (xup->xc_handle = xc_interface_open()) == -1 )
- {
- PyErr_SetString(port_error, "Could not open Xen control interface");
+
+ kmsg.type = XCS_CIF_NEW_CC;
+ kmsg.u.interface.dom = xup->remote_dom;
+ kmsg.u.interface.local_port = port1;
+ kmsg.u.interface.remote_port = port2;
+ xcs_ctrl_send(&kmsg);
+ xcs_ctrl_read(&kmsg);
+
+ if ( kmsg.result != XCS_RSLT_OK )
goto fail1;
- }
-
- if ( dom == 0 )
- {
- /*
- * The control-interface event channel for DOM0 is already set up.
- * We use an ioctl to discover the port at our end of the channel.
- */
- port1 = ioctl(xup->xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN, NULL);
- port2 = -1; /* We don't need the remote end of the DOM0 link. */
- if ( port1 < 0 )
- {
- PyErr_SetString(port_error, "Could not open channel to DOM0");
- goto fail2;
- }
- }
- else if ( xc_evtchn_bind_interdomain(xup->xc_handle,
- DOMID_SELF, dom,
- &port1, &port2) != 0 )
- {
- PyErr_SetString(port_error, "Could not open channel to domain");
- goto fail2;
- }
-
- xup->local_port = port1;
- xup->remote_port = port2;
-
- if ( __xu_port_connect(xup) != 0 )
- goto fail3;
-
+
+ xup->local_port = kmsg.u.interface.local_port;
+ xup->remote_port = kmsg.u.interface.remote_port;
+ xup->connected = 1;
+
return (PyObject *)xup;
-
- fail3:
- if ( dom != 0 )
- (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, port1);
- fail2:
- (void)xc_interface_close(xup->xc_handle);
+
fail1:
PyObject_Del((PyObject *)xup);
- return NULL;
+ return NULL;
}
static PyObject *xu_port_getattr(PyObject *obj, char *name)
@@ -1084,11 +1442,20 @@ static PyObject *xu_port_getattr(PyObject *obj, char *name)
static void xu_port_dealloc(PyObject *self)
{
+
xu_port_object *xup = (xu_port_object *)self;
- __xu_port_disconnect(xup);
+ xcs_msg_t kmsg;
+
if ( xup->remote_dom != 0 )
- (void)xc_evtchn_close(xup->xc_handle, DOMID_SELF, xup->local_port);
- (void)xc_interface_close(xup->xc_handle);
+ {
+ kmsg.type = XCS_CIF_FREE_CC;
+ kmsg.u.interface.dom = xup->remote_dom;
+ kmsg.u.interface.local_port = xup->local_port;
+ kmsg.u.interface.remote_port = xup->remote_port;
+ xcs_ctrl_send(&kmsg);
+ xcs_ctrl_read(&kmsg);
+ }
+
PyObject_Del(self);
}
@@ -1391,7 +1758,7 @@ static PyMethodDef xu_methods[] = {
"Create a new notifier." },
{ "message", xu_message_new, METH_VARARGS,
"Create a new communications message." },
- { "port", xu_port_new, METH_VARARGS,
+ { "port", (PyCFunction)xu_port_new, METH_VARARGS | METH_KEYWORDS,
"Create a new communications port." },
{ "buffer", xu_buffer_new, METH_VARARGS,
"Create a new ring buffer." },
@@ -1409,4 +1776,8 @@ PyMODINIT_FUNC initxu(void)
d = PyModule_GetDict(m);
port_error = PyErr_NewException(XENPKG ".PortError", NULL, NULL);
PyDict_SetItemString(d, "PortError", port_error);
+
+ /* KAF: This ensures that we get debug output in a timely manner. */
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
}
diff --git a/tools/python/xen/sv/CreateDomain.py b/tools/python/xen/sv/CreateDomain.py
index b4fd52f0e8..4378897e5c 100644
--- a/tools/python/xen/sv/CreateDomain.py
+++ b/tools/python/xen/sv/CreateDomain.py
@@ -16,7 +16,7 @@ class CreateDomain( Wizard ):
CreatePage4,
CreateFinish ]
- Wizard.__init__( self, urlWriter, "Create Domain Wizard", sheets )
+ Wizard.__init__( self, urlWriter, "Create Domain", sheets )
class CreatePage0( Sheet ):
@@ -25,13 +25,15 @@ class CreatePage0( Sheet ):
self.addControl( InputControl( 'name', 'VM Name', 'VM Name:', "[\\w|\\S]+", "You must enter a name in this field" ) )
self.addControl( InputControl( 'memory', '64', 'Memory (Mb):', "[\\d]+", "You must enter a number in this field" ) )
self.addControl( InputControl( 'cpu', '0', 'CPU:', "[\\d]+", "You must enter a number in this feild" ) )
+ self.addControl( InputControl( 'cpu_weight', '1', 'CPU Weight:', "[\\d]+", "You must enter a number in this feild" ) )
class CreatePage1( Sheet ):
def __init__( self, urlWriter ):
Sheet.__init__( self, urlWriter, "Setup Kernel Image", 1 )
- self.addControl( ListControl( 'builder', [('linux', 'Linux'), ('netbsd', 'NetBSD')], 'Kernel Type:' ) )
- self.addControl( FileControl( 'kernel', '/boot/vmlinuz-2.4.26-xenU', 'Kernel Image:' ) )
+# For now we don't need to select a builder...
+# self.addControl( ListControl( 'builder', [('linux', 'Linux'), ('netbsd', 'NetBSD')], 'Kernel Type:' ) )
+ self.addControl( FileControl( 'kernel', '/boot/vmlinuz-2.6.9-xenU', 'Kernel Image:' ) )
self.addControl( InputControl( 'extra', '', 'Kernel Command Line Parameters:' ) )
class CreatePage2( Sheet ):
@@ -83,9 +85,16 @@ class CreateFinish( Sheet ):
xend_sxp = self.translate_sxp( string2sxp( self.passback ) )
- dom_sxp = server.xend_domain_create( xend_sxp )
+ try:
+ dom_sxp = server.xend_domain_create( xend_sxp )
+ success = "Your domain was successfully created.\n"
+ except:
+ success = "There was an error creating your domain.\nThe configuration used is as follows:\n"
+ dom_sxp = xend_sxp
+
+
- pt = PreTab( sxp2prettystring( dom_sxp ) )
+ pt = PreTab( success + sxp2prettystring( dom_sxp ) )
pt.write_BODY( request )
request.write( "<input type='hidden' name='passback' value=\"%s\"></p>" % self.passback )
@@ -105,7 +114,9 @@ class CreateFinish( Sheet ):
vals.name = get( 'name' )
vals.memory = get( 'memory' )
+ vals.maxmem = get( 'maxmem' )
vals.cpu = get( 'cpu' )
+ vals.cpu_weight = get( 'cpu_weight' )
vals.builder = get( 'builder' )
vals.kernel = get( 'kernel' )
@@ -121,7 +132,7 @@ class CreateFinish( Sheet ):
vals.disk = vbds
- #misc crap
+ #misc
vals.pci = []
@@ -145,4 +156,8 @@ class CreateFinish( Sheet ):
vals.cmdline_ip = "%s:%s:%s:%s:%s:eth0:%s" % (ip, nfs, gate, mask, host, dhcp)
- return make_config( vals )
+ try:
+ return make_config( vals )
+ except:
+ return [["Error creating domain config."]]
+
diff --git a/tools/python/xen/sv/Daemon.py b/tools/python/xen/sv/Daemon.py
index fdbc6145dc..5a8d18e5e4 100644
--- a/tools/python/xen/sv/Daemon.py
+++ b/tools/python/xen/sv/Daemon.py
@@ -15,6 +15,8 @@ from xen.sv.params import *
from twisted.internet import reactor
from twisted.web import static, server, script
+from xen.util.ip import _readline, _readlines
+
class Daemon:
"""The xend daemon.
"""
@@ -57,7 +59,7 @@ class Daemon:
return 0
# Read the pid of the previous invocation and search active process list.
pid = open(PID_FILE, 'r').read()
- lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
+ lines = _readlines(os.popen('ps ' + pid + ' 2>/dev/null'))
for line in lines:
if re.search('^ *' + pid + '.+xensv', line):
if not kill:
@@ -96,7 +98,7 @@ class Daemon:
reactor.run()
def exit(self):
- reactor.diconnectAll()
+ reactor.disconnectAll()
sys.exit(0)
def instance():
diff --git a/tools/python/xen/sv/DomInfo.py b/tools/python/xen/sv/DomInfo.py
index 6f8ac1ab5b..8aabb19057 100755
--- a/tools/python/xen/sv/DomInfo.py
+++ b/tools/python/xen/sv/DomInfo.py
@@ -1,10 +1,13 @@
-from xen.xend.XendClient import aserver as server
+from xen.xend.XendClient import getAsynchServer
+server = getAsynchServer()
from xen.xend import PrettyPrint
from xen.sv.HTMLBase import HTMLBase
from xen.sv.util import *
from xen.sv.GenTabbed import *
+DEBUG=1
+
class DomInfo( GenTabbed ):
def __init__( self, urlWriter ):
@@ -76,8 +79,11 @@ class DomSXPTab( PreTab ):
request.write( "<p>Please Select a Domain</p>" )
return None
- domInfo = server.xend_domain( self.dom )
-
+ try:
+ domInfo = server.xend_domain( self.dom )
+ except:
+ domInfo = [["Error getting domain details."]]
+
self.source = sxp2prettystring( domInfo )
PreTab.write_BODY( self, request )
@@ -88,32 +94,54 @@ class DomActionTab( ActionTab ):
actions = { "shutdown" : ( "Shutdown the Domain", "shutdown.png" ),
"reboot" : ( "Reboot the Domain", "reboot.png" ),
"pause" : ( "Pause the Domain", "pause.png" ),
- "unpause" : ( "Unpause the Domain", "unpause.png" ) }
+ "unpause" : ( "Unpause the Domain", "unpause.png" ),
+ "destroy" : ( "Destroy the Domain", "destroy.png" ) }
ActionTab.__init__( self, actions )
def op_shutdown( self, request ):
dom = getVar( 'dom', request )
- if not dom is None:
- print ">DomShutDown %s" % dom
- #server.xend_node_shutdown()
+ if not dom is None and dom != '0':
+ if DEBUG: print ">DomShutDown %s" % dom
+ try:
+ server.xend_domain_shutdown( int( dom ), "halt" )
+ except:
+ pass
def op_reboot( self, request ):
dom = getVar( 'dom', request )
- if not dom is None:
- print ">DomReboot %s" % dom
- #server.xend_node_reboot()
-
+ if not dom is None and dom != '0':
+ if DEBUG: print ">DomReboot %s" % dom
+ try:
+ server.xend_domain_shutdown( int( dom ), "reboot" )
+ except:
+ pass
+
def op_pause( self, request ):
dom = getVar( 'dom', request )
- if not dom is None:
- print ">DomPause %s" % dom
- server.xend_domain_pause( int( dom ) )
-
+ if not dom is None and dom != '0':
+ if DEBUG: print ">DomPause %s" % dom
+ try:
+ server.xend_domain_pause( int( dom ) )
+ except:
+ pass
+
def op_unpause( self, request ):
dom = getVar( 'dom', request )
- if not dom is None:
- print ">DomUnpause %s" % dom
- server.xend_domain_unpause( int( dom ) )
+ if not dom is None and dom != '0':
+ if DEBUG: print ">DomUnpause %s" % dom
+ try:
+ server.xend_domain_unpause( int( dom ) )
+ except:
+ pass
+
+ def op_destroy( self, request ):
+ dom = getVar( 'dom', request )
+ if not dom is None and dom != '0':
+ if DEBUG: print ">DomDestroy %s" % dom
+ try:
+ server.xend_domain_destroy( int( dom ), "halt" )
+ except:
+ pass
diff --git a/tools/python/xen/sv/DomList.py b/tools/python/xen/sv/DomList.py
index faed020b05..86976b7af3 100755
--- a/tools/python/xen/sv/DomList.py
+++ b/tools/python/xen/sv/DomList.py
@@ -17,9 +17,14 @@ class DomList( HTMLBase ):
def write_BODY( self, request, head=True, long=True ):
- domains = map(int, server.xend_domains())
- domains.sort()
+ domains = None
+ try:
+ domains = server.xend_domains()
+ domains.sort()
+ except:
+ pass
+
request.write( "\n<table style='border:0px solid white' cellspacing='0' cellpadding='0' border='0' width='100%'>\n" )
if head:
@@ -29,28 +34,42 @@ class DomList( HTMLBase ):
odd = True
- for domain in domains:
- if odd:
- request.write( "<tr class='domainInfoOdd'>\n" )
- odd = False
- else:
- request.write( "<tr class='domainInfoEven'>\n" )
- odd = True
- self.write_DOMAIN( request, getDomInfoHash( domain ), long )
- request.write( "</tr>\n" )
-
+ if not domains is None:
+ for domain in domains:
+ if odd:
+ request.write( "<tr class='domainInfoOdd'>\n" )
+ odd = False
+ else:
+ request.write( "<tr class='domainInfoEven'>\n" )
+ odd = True
+ self.write_DOMAIN( request, getDomInfoHash( domain ), long )
+ request.write( "</tr>\n" )
+ else:
+ request.write( "<tr colspan='10'><p class='small'>Error getting domain list<br/>Perhaps XenD not running?</p></tr>")
+
request.write( "</table>\n" )
def write_DOMAIN( self, request, domInfoHash, long=True ):
- request.write( "<td class='domainInfo' align='center'>%(dom)-4d</td>\n" % domInfoHash )
+ request.write( "<td class='domainInfo' align='center'>%(id)s</td>\n" % domInfoHash )
- url = self.urlWriter( "&mod=info&dom=%(dom)-4d" % domInfoHash )
+ url = self.urlWriter( "&mod=info&dom=%(id)s" % domInfoHash )
request.write( "<td class='domainInfo' align='center'><a href='%s'>%s</a></td>\n" % ( url, domInfoHash['name'] ) )
if long:
request.write( "<td class='domainInfo' align='center'>%(memory)5s</td>\n" % domInfoHash )
request.write( "<td class='domainInfo' align='center'>%(cpu)2s</td>\n" % domInfoHash )
request.write( "<td class='domainInfo' align='center'>%(state)5s</td>\n" % domInfoHash )
+ if domInfoHash[ 'id' ] != "0":
+ request.write( "<td class='domainInfo' align='center'>" )
+
+ if domInfoHash[ 'state' ][ 2 ] == "-":
+ request.write( "<img src='images/small-pause.png' onclick='doOp2( \"pause\", \"%(dom)-4s\" )'>" % domInfoHash )
+ else:
+ request.write( "<img src='images/small-unpause.png' onclick='doOp2( \"unpause\", \"%(dom)-4s\" )'>" % domInfoHash )
+
+ request.write( "<img src='images/small-destroy.png' onclick='doOp2( \"destroy\", \"%(dom)-4s\" )'></td>" % domInfoHash)
+ else:
+ request.write( "<td>&nbsp;</td>" )
def write_DOMAIN_HEAD( self, request, long=True ):
request.write( "<td class='domainInfoHead' align='center'>Domain</td>\n" )
@@ -59,4 +78,4 @@ class DomList( HTMLBase ):
request.write( "<td class='domainInfoHead' align='center'>Memory / Mb</td>\n" )
request.write( "<td class='domainInfoHead' align='center'>CPU</td>\n" )
request.write( "<td class='domainInfoHead' align='center'>State</td>\n" )
-
+ request.write( "<td class='domainInfoHead' align='center'></td>\n" )
diff --git a/tools/python/xen/sv/HTMLBase.py b/tools/python/xen/sv/HTMLBase.py
index 10d860e0c4..e67784d558 100755
--- a/tools/python/xen/sv/HTMLBase.py
+++ b/tools/python/xen/sv/HTMLBase.py
@@ -1,4 +1,5 @@
from twisted.web.resource import Resource
+from xen.sv.util import *
class HTMLBase( Resource ):
@@ -28,6 +29,7 @@ class HTMLBase( Resource ):
def write_BOTTOM( self, request ):
request.write('<input type="hidden" name="op" value="">')
+ request.write('<input type="hidden" name="args" value="">')
request.write('</form>')
request.write( "</body></html>" )
diff --git a/tools/python/xen/sv/Main.py b/tools/python/xen/sv/Main.py
index bf6119c600..196e1c1450 100755
--- a/tools/python/xen/sv/Main.py
+++ b/tools/python/xen/sv/Main.py
@@ -3,6 +3,11 @@ from xen.sv.DomList import DomList
from xen.sv.NodeInfo import NodeInfo
from xen.sv.DomInfo import DomInfo
from xen.sv.CreateDomain import CreateDomain
+from xen.sv.MigrateDomain import MigrateDomain
+from xen.sv.SaveDomain import SaveDomain
+from xen.sv.RestoreDomain import RestoreDomain
+
+from xen.xend.XendClient import server
from xen.sv.util import getVar
@@ -14,20 +19,31 @@ class Main( HTMLBase ):
self.modules = { "node": NodeInfo,
"list": DomList,
"info": DomInfo,
- "create": CreateDomain }
+ "create": CreateDomain,
+ "migrate" : MigrateDomain,
+ "save" : SaveDomain,
+ "restore" : RestoreDomain }
+
+ # ordered list of module menus to display
+ self.module_menus = [ "node", "create", "migrate", "save",
+ "restore", "list" ]
HTMLBase.__init__(self)
def render_POST( self, request ):
#decide what module post'd the action
+
+ args = getVar( 'args', request )
mod = getVar( 'mod', request )
- if not mod is None:
+ if not mod is None and args is None:
module = self.modules[ mod ]
#check module exists
if module:
- module( self.mainUrlWriter ).perform( request )
+ module( self.mainUrlWriter ).perform( request )
+ else:
+ self.perform( request )
return self.render_GET( request )
@@ -47,10 +63,10 @@ class Main( HTMLBase ):
request.write( " <img src='images/xen.png' width='150' height='75' border='0'/></a><br/></td></tr>" )
request.write( " <tr><td height='60px' align='center'><p class='small'>SV Web Interface<br/>(C) <a href='mailto:tw275@cam.ac.uk'>Tom Wilkie</a> 2004</p></td></tr>")
request.write( " <tr><td align='center' valign='top'>" )
-
- for (modName, module) in self.modules.items():
- module( self.mainUrlWriter( modName ) ).write_MENU( request )
+ for modName in self.module_menus:
+ self.modules[modName]( self.mainUrlWriter( modName ) ).write_MENU( request )
+
request.write( " </td></tr>" )
request.write( " </table>" )
request.write( " &nbsp;" )
@@ -80,3 +96,18 @@ class Main( HTMLBase ):
request.write( "</table>\n" )
+
+ def op_destroy( self, request ):
+ dom = getVar( 'dom', request )
+ if not dom is None and dom != "0":
+ server.xend_domain_destroy( int( dom ), "halt" )
+
+ def op_pause( self, request ):
+ dom = getVar( 'dom', request )
+ if not dom is None and dom != "0":
+ server.xend_domain_pause( int( dom ) )
+
+ def op_unpause( self, request ):
+ dom = getVar( 'dom', request )
+ if not dom is None and dom != "0":
+ server.xend_domain_unpause( int( dom ) )
diff --git a/tools/python/xen/sv/MigrateDomain.py b/tools/python/xen/sv/MigrateDomain.py
new file mode 100644
index 0000000000..928acf3f42
--- /dev/null
+++ b/tools/python/xen/sv/MigrateDomain.py
@@ -0,0 +1,72 @@
+from xen.sv.Wizard import *
+from xen.sv.util import *
+from xen.sv.GenTabbed import PreTab
+
+from xen.xm.create import make_config, OptVals
+
+from xen.xend.XendClient import server
+
+class MigrateDomain( Wizard ):
+ def __init__( self, urlWriter ):
+
+ sheets = [ ChooseMigrateDomain,
+ DoMigrate ]
+
+ Wizard.__init__( self, urlWriter, "Migrate Domain", sheets )
+
+
+class ChooseMigrateDomain( Sheet ):
+ def __init__( self, urlWriter ):
+ Sheet.__init__( self, urlWriter, "Configure Migration", 0)
+ try:
+ domains = server.xend_domains()
+ domains.sort()
+ except:
+ pass
+
+ domnames = []
+ for i in domains:
+ if i != 'Domain-0': domnames.append((i,i))
+
+ self.addControl( ListControl('domid',
+ domnames,
+ 'Domain ID:') )
+ self.addControl( TickControl('live',
+ 'True',
+ 'Live migrate:') )
+ self.addControl( InputControl('rate',
+ '0',
+ 'Rate limit:') )
+ self.addControl( InputControl( 'dest', 'myhost.mydomain',
+ 'Name or IP address:',
+ ".*") )
+
+class DoMigrate( Sheet ):
+ def __init__(self, urlWriter ):
+ Sheet.__init__(self, urlWriter, "Migration Done", 1)
+
+ def write_BODY( self, request, err ):
+
+ if not self.passback: self.parseForm( request )
+
+# print string2sxp(self.passback)
+
+ config = ssxp2hash ( string2sxp( self.passback ) )
+
+ try:
+ print config
+ print config['domid'], config['dest']
+ dom_sxp = server.xend_domain_migrate( config['domid'],
+ config['dest'],
+ config.get('live') == 'True',
+ config['rate'] )
+ success = "Your domain was successfully Migrated.\n"
+ except Exception, e:
+ success = "There was an error migrating your domain\n"
+ dom_sxp = str(e)
+
+ pt = PreTab( success + dom_sxp ) # sxp2prettystring( dom_sxp ) )
+ pt.write_BODY( request )
+
+ request.write( "<input type='hidden' name='passback' value=\"%s\"></p>" % self.passback )
+ request.write( "<input type='hidden' name='sheet' value='%s'></p>" % self.location )
diff --git a/tools/python/xen/sv/NodeInfo.py b/tools/python/xen/sv/NodeInfo.py
index 948acef4b6..5db1a34c50 100755
--- a/tools/python/xen/sv/NodeInfo.py
+++ b/tools/python/xen/sv/NodeInfo.py
@@ -20,8 +20,12 @@ class NodeInfoTab( GeneralTab ):
def __init__( self ):
- nodeInfo = sxp2hash( server.xend_node() )
-
+ nodeInfo = {}
+ try:
+ nodeInfo = sxp2hash( server.xend_node() )
+ except:
+ nodeInfo[ 'system' ] = 'Error getting node info'
+
dictTitles = {}
dictTitles[ 'System' ] = 'system'
dictTitles[ 'Hostname' ] = 'host'
@@ -39,8 +43,11 @@ class NodeInfoTab( GeneralTab ):
class NodeDmesgTab( PreTab ):
def __init__( self ):
- dmesg = server.xend_node_dmesg()
- PreTab.__init__( self, dmesg[ 1 ] )
+ try:
+ dmesg = server.xend_node_get_dmesg()
+ except:
+ dmesg = "Error getting node information: XenD not running?"
+ PreTab.__init__( self, dmesg )
class NodeActionTab( ActionTab ):
@@ -50,8 +57,8 @@ class NodeActionTab( ActionTab ):
def op_shutdown( self, request ):
print ">NodeShutDown"
- #server.xend_node_shutdown()
+ server.xend_node_shutdown()
def op_reboot( self, request ):
print ">NodeReboot"
- #server.xend_node_reboot()
+ server.xend_node_reboot()
diff --git a/tools/python/xen/sv/RestoreDomain.py b/tools/python/xen/sv/RestoreDomain.py
new file mode 100644
index 0000000000..be8b4f558a
--- /dev/null
+++ b/tools/python/xen/sv/RestoreDomain.py
@@ -0,0 +1,46 @@
+from xen.sv.Wizard import *
+from xen.sv.util import *
+from xen.sv.GenTabbed import PreTab
+
+from xen.xm.create import make_config, OptVals
+
+from xen.xend.XendClient import server
+
+class RestoreDomain( Wizard ):
+ def __init__( self, urlWriter ):
+
+ sheets = [ ChooseRestoreDomain,
+ DoRestore ]
+
+ Wizard.__init__( self, urlWriter, "Restore Domain", sheets )
+
+
+class ChooseRestoreDomain( Sheet ):
+ def __init__( self, urlWriter ):
+ Sheet.__init__( self, urlWriter, "Configure Restore", 0)
+
+ self.addControl( InputControl( 'file', '',
+ 'Suspend file name:',
+ ".*") )
+
+class DoRestore( Sheet ):
+ def __init__(self, urlWriter ):
+ Sheet.__init__(self, urlWriter, "Restore Done", 1)
+
+ def write_BODY( self, request, err ):
+
+ if not self.passback: self.parseForm( request )
+ config = ssxp2hash ( string2sxp( self.passback ) )
+
+ try:
+ dom_sxp = server.xend_domain_restore( config['file'] )
+ success = "Your domain was successfully restored.\n"
+ except Exception, e:
+ success = "There was an error restoring your domain\n"
+ dom_sxp = str(e)
+
+ pt = PreTab( success + sxp2prettystring( dom_sxp ) )
+ pt.write_BODY( request )
+
+ request.write( "<input type='hidden' name='passback' value=\"%s\"></p>" % self.passback )
+ request.write( "<input type='hidden' name='sheet' value='%s'></p>" % self.location )
diff --git a/tools/python/xen/sv/SaveDomain.py b/tools/python/xen/sv/SaveDomain.py
new file mode 100644
index 0000000000..4c4e315272
--- /dev/null
+++ b/tools/python/xen/sv/SaveDomain.py
@@ -0,0 +1,59 @@
+from xen.sv.Wizard import *
+from xen.sv.util import *
+from xen.sv.GenTabbed import PreTab
+
+from xen.xm.create import make_config, OptVals
+
+from xen.xend.XendClient import server
+
+class SaveDomain( Wizard ):
+ def __init__( self, urlWriter ):
+
+ sheets = [ ChooseSaveDomain,
+ DoSave ]
+
+ Wizard.__init__( self, urlWriter, "Save Domain", sheets )
+
+
+class ChooseSaveDomain( Sheet ):
+ def __init__( self, urlWriter ):
+ Sheet.__init__( self, urlWriter, "Configure Save", 0)
+ try:
+ domains = server.xend_domains()
+ domains.sort()
+ except:
+ pass
+
+ domnames = []
+ for i in domains:
+ if i != 'Domain-0': domnames.append((i,i))
+
+ self.addControl( ListControl('domid',
+ domnames,
+ 'Domain ID:') )
+ self.addControl( InputControl( 'file', '',
+ 'Suspend file name:',
+ ".*") )
+
+class DoSave( Sheet ):
+ def __init__(self, urlWriter ):
+ Sheet.__init__(self, urlWriter, "Save Done", 1)
+
+ def write_BODY( self, request, err ):
+
+ if not self.passback: self.parseForm( request )
+ config = ssxp2hash ( string2sxp( self.passback ) )
+
+ try:
+ dom_sxp = server.xend_domain_save( config['domid'],
+ config['file'] )
+ success = "Your domain was successfully saved.\n"
+ except Exception, e:
+ success = "There was an error saving your domain\n"
+ dom_sxp = str(e)
+
+ pt = PreTab( success + dom_sxp ) # sxp2prettystring( dom_sxp ) )
+ pt.write_BODY( request )
+
+ request.write( "<input type='hidden' name='passback' value=\"%s\"></p>" % self.passback )
+ request.write( "<input type='hidden' name='sheet' value='%s'></p>" % self.location )
diff --git a/tools/python/xen/sv/params.py b/tools/python/xen/sv/params.py
index 16b764f088..beed647a4f 100644
--- a/tools/python/xen/sv/params.py
+++ b/tools/python/xen/sv/params.py
@@ -1,3 +1,3 @@
SV_PORT = 8080
-SV_ROOT = "/var/xen/sv/"
-PID_FILE = "/var/xen/sv.pid" \ No newline at end of file
+SV_ROOT = "/var/lib/xen/sv/"
+PID_FILE = "/var/run/xen-sv.pid"
diff --git a/tools/python/xen/sv/util.py b/tools/python/xen/sv/util.py
index b332b60482..3207b8f0cc 100755
--- a/tools/python/xen/sv/util.py
+++ b/tools/python/xen/sv/util.py
@@ -5,8 +5,12 @@ from xen.xend import PrettyPrint
import types
def getDomInfoHash( domain ):
- domInfoHash = sxp2hash( server.xend_domain( int( domain ) ) )
- domInfoHash['dom'] = int( domain )
+ domInfoHash = {}
+ try:
+ domInfoHash = sxp2hash( server.xend_domain( domain ) )
+ domInfoHash['dom'] = domain
+ except:
+ domInfoHash['name'] = "Error getting domain details"
return domInfoHash
def sxp2hash( s ):
diff --git a/tools/python/xen/util/console_client.py b/tools/python/xen/util/console_client.py
index 7ac63aeb75..8bd3178eab 100644
--- a/tools/python/xen/util/console_client.py
+++ b/tools/python/xen/util/console_client.py
@@ -27,24 +27,34 @@ def __recv_from_sock(sock):
while not stop:
try:
data = sock.recv(1024)
- os.write(1, data)
except socket.error, error:
if error[0] != errno.EINTR:
raise
+ else:
+ try:
+ os.write(1, data)
+ except os.error, error:
+ if error[0] != errno.EINTR:
+ raise
os.wait()
def __send_to_sock(sock):
while 1:
- data = os.read(0,1024)
- if ord(data[0]) == ord(']')-64:
- break
try:
- sock.send(data)
- except socket.error, error:
- if error[0] == errno.EPIPE:
- sys.exit(0)
+ data = os.read(0,1024)
+ except os.error, error:
if error[0] != errno.EINTR:
raise
+ else:
+ if ord(data[0]) == ord(']')-64:
+ break
+ try:
+ sock.send(data)
+ except socket.error, error:
+ if error[0] == errno.EPIPE:
+ sys.exit(0)
+ if error[0] != errno.EINTR:
+ raise
sys.exit(0)
def connect(host,port):
diff --git a/tools/python/xen/util/memmap.py b/tools/python/xen/util/memmap.py
new file mode 100644
index 0000000000..2899a87535
--- /dev/null
+++ b/tools/python/xen/util/memmap.py
@@ -0,0 +1,41 @@
+mem_caching_attr = {
+ 'UC' : 0,
+ 'WC' : 1,
+ 'WT' : 4,
+ 'WP' : 5,
+ 'WB' : 6,
+ };
+
+e820_mem_type = {
+ 'AddressRangeMemory' : 1,
+ 'AddressRangeReserved' : 2,
+ 'AddressRangeACPI' : 3,
+ 'AddressRangeNVS' : 4,
+ 'AddressRangeIO' : 16,
+ 'AddressRangeShared' : 17,
+};
+
+MT_COL = 2
+MA_COL = 3
+
+def strmap(row):
+ if (type(row) != type([])):
+ return row
+ row[MT_COL] = e820_mem_type[row[MT_COL]]
+ row[MA_COL] = mem_caching_attr[row[MA_COL]]
+ return row
+
+def memmap_parse(memmap):
+ return map(strmap, memmap)
+
+if __name__ == '__main__':
+ memmap = [ 'memmap',
+ [ '1', '2', 'AddressRangeMemory', 'UC'],
+ [ '1', '2', 'AddressRangeReserved', 'UC'],
+ [ '1', '2', 'AddressRangeACPI', 'WB'],
+ [ '1', '2', 'AddressRangeNVS', 'WB'],
+ [ '1', '2', 'AddressRangeIO', 'WB'],
+ [ '1', '2', 'AddressRangeShared', 'WB']]
+ print memmap_parse(memmap);
+
+
diff --git a/tools/python/xen/xend/Blkctl.py b/tools/python/xen/xend/Blkctl.py
new file mode 100644
index 0000000000..d90c7ce51d
--- /dev/null
+++ b/tools/python/xen/xend/Blkctl.py
@@ -0,0 +1,45 @@
+"""Xend interface to block control scripts.
+"""
+import os
+import os.path
+import sys
+import string
+
+from xen.xend import XendRoot
+
+from xen.util.ip import _readline, _readlines
+
+xroot = XendRoot.instance()
+
+"""Where network control scripts live."""
+SCRIPT_DIR = xroot.block_script_dir
+
+def block(op, type, dets, script=None):
+ """Call a block control script.
+ Xend calls this with op 'bind' when it is about to export a block device
+ (other than a raw partition). The script is called with unbind when a
+ device is no longer in use and should be removed.
+
+ @param op: operation (start, stop, status)
+ @param type: type of block device (determines the script used)
+ @param dets: arguments to the control script
+ @param script: block script name
+ """
+
+ if op not in ['bind', 'unbind']:
+ raise ValueError('Invalid operation:' + op)
+
+ # Special case phy devices - they don't require any (un)binding
+ if type == 'phy':
+ return dets
+
+ if script is None:
+ script = xroot.get_block_script(type)
+ script = os.path.join(SCRIPT_DIR, script)
+ args = [op] + string.split(dets, ':')
+ args = ' '.join(args)
+ out = os.popen(script + ' ' + args)
+
+ output = _readline(out)
+ out.close()
+ return string.rstrip(output)
diff --git a/tools/python/xen/xend/XendAsynchProtocol.py b/tools/python/xen/xend/XendAsynchProtocol.py
new file mode 100644
index 0000000000..6afaf14285
--- /dev/null
+++ b/tools/python/xen/xend/XendAsynchProtocol.py
@@ -0,0 +1,94 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from twisted.protocols import http
+from twisted.internet.protocol import ClientCreator
+from twisted.internet.defer import Deferred
+from twisted.internet import reactor
+
+from XendProtocol import XendClientProtocol, XendRequest
+
+class AsynchXendClient(http.HTTPClient):
+ """A subclass of twisted's HTTPClient to deal with a connection to xend.
+ Makes the request when connected, and delegates handling responses etc.
+ to its protocol (usually an AsynchXendClientProtocol instance).
+ """
+ def __init__(self, protocol, request):
+ self.protocol = protocol
+ self.request = request
+
+ def connectionMade(self):
+ request = self.request
+ url = self.request.url
+ self.sendCommand(request.method, url.fullpath())
+ self.sendHeader('Host', url.location())
+ for (k, v) in request.headers.items():
+ self.sendHeader(k, v)
+ if request.data:
+ self.sendHeader('Content-Length', len(request.data))
+ self.endHeaders()
+ if request.data:
+ self.transport.write(request.data)
+
+ def handleStatus(self, version, status, message):
+ return self.protocol.handleStatus(version, status, message)
+
+ def handleHeader(self, key, val):
+ return self.protocol.handleHeader(key, val)
+
+ def handleResponse(self, data):
+ return self.protocol.handleResponse(data)
+
+class AsynchXendClientProtocol(XendClientProtocol):
+ """An asynchronous xend client. Uses twisted to connect to xend
+ and make the request. It does not block waiting for the result,
+ but sets up a deferred that is called when the result becomes available.
+
+ Uses AsynchXendClient to manage the connection.
+ """
+ def __init__(self):
+ self.err = None
+ self.headers = {}
+
+ def xendRequest(self, url, method, args=None):
+ """Make a request to xend. The returned deferred is called when
+ the result is available.
+
+ @param url: xend request url
+ @param method: http method: POST or GET
+ @param args: request arguments (dict)
+ @return: deferred
+ """
+ request = XendRequest(url, method, args)
+ self.deferred = Deferred()
+ clientCreator = ClientCreator(reactor, AsynchXendClient, self, request)
+ clientCreator.connectTCP(url.host, url.port)
+ return self.deferred
+
+ def callErrback(self, err):
+ if not self.deferred.called:
+ self.err = err
+ self.deferred.errback(err)
+ return err
+
+ def callCallback(self, val):
+ if not self.deferred.called:
+ self.deferred.callback(val)
+ return val
+
+ def handleException(self, err):
+ return self.callErrback(err)
+
+ def handleHeader(self, key, val):
+ self.headers[key.lower()] = val
+
+ def getHeader(self, key):
+ return self.headers.get(key.lower())
+
+ def handleResponse(self, data):
+ if self.err: return self.err
+ val = XendClientProtocol.handleResponse(self, data)
+ if isinstance(val, Exception):
+ self.callErrback(val)
+ else:
+ self.callCallback(val)
+ return val
diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py
index 09ea1cc2ea..4e733b7fde 100644
--- a/tools/python/xen/xend/XendClient.py
+++ b/tools/python/xen/xend/XendClient.py
@@ -11,27 +11,14 @@ is accessible via this API.
"""
import os
import sys
-import httplib
import types
-from StringIO import StringIO
-
-from twisted.protocols import http
-from twisted.internet.protocol import ClientCreator
-from twisted.internet.defer import Deferred
-from twisted.internet import reactor
-
-from encode import *
import sxp
import PrettyPrint
+from XendProtocol import XendClientProtocol, SynchXendClientProtocol, XendError
DEBUG = 0
-class XendError(RuntimeError):
- """Error class for 'expected errors' when talking to xend.
- """
- pass
-
def fileof(val):
"""Converter for passing configs or other 'large' data.
Handles lists, files directly.
@@ -102,229 +89,6 @@ class URL:
query=query,
frag=frag)
-class XendRequest:
- """A request to xend.
- """
-
- def __init__(self, url, method, args):
- """Create a request. Sets up the headers, argument data, and the
- url.
-
- @param url: the url to request
- @param method: request method, GET or POST
- @param args: dict containing request args, if any
- """
- if url.proto != 'http':
- raise ValueError('Invalid protocol: ' + url.proto)
- (hdr, data) = encode_data(args)
- if args and method == 'GET':
- url.query = data
- data = None
- if method == "POST" and url.path.endswith('/'):
- url.path = url.path[:-1]
-
- self.headers = hdr
- self.data = data
- self.url = url
- self.method = method
-
-class XendClientProtocol:
- """Abstract class for xend clients.
- """
- def xendRequest(self, url, method, args=None):
- """Make a request to xend.
- Implement in a subclass.
-
- @param url: xend request url
- @param method: http method: POST or GET
- @param args: request arguments (dict)
- """
- raise NotImplementedError()
-
- def xendGet(self, url, args=None):
- """Make a xend request using HTTP GET.
- Requests using GET are usually 'safe' and may be repeated without
- nasty side-effects.
-
- @param url: xend request url
- @param data: request arguments (dict)
- """
- return self.xendRequest(url, "GET", args)
-
- def xendPost(self, url, args):
- """Make a xend request using HTTP POST.
- Requests using POST potentially cause side-effects, and should
- not be repeated unless you really want to repeat the side
- effect.
-
- @param url: xend request url
- @param args: request arguments (dict)
- """
- return self.xendRequest(url, "POST", args)
-
- def handleStatus(self, version, status, message):
- """Handle the status returned from the request.
- """
- status = int(status)
- if status in [ http.NO_CONTENT ]:
- return None
- if status not in [ http.OK, http.CREATED, http.ACCEPTED ]:
- return self.handleException(XendError(message))
- return 'ok'
-
- def handleResponse(self, data):
- """Handle the data returned in response to the request.
- """
- if data is None: return None
- type = self.getHeader('Content-Type')
- if type != sxp.mime_type:
- return data
- try:
- pin = sxp.Parser()
- pin.input(data);
- pin.input_eof()
- val = pin.get_val()
- except sxp.ParseError, err:
- return self.handleException(err)
- if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
- err = XendError(val[1])
- return self.handleException(err)
- return val
-
- def handleException(self, err):
- """Handle an exception during the request.
- May be overridden in a subclass.
- """
- raise err
-
- def getHeader(self, key):
- """Get a header from the response.
- Case is ignored in the key.
-
- @param key: header key
- @return: header
- """
- raise NotImplementedError()
-
-class SynchXendClientProtocol(XendClientProtocol):
- """A synchronous xend client. This will make a request, wait for
- the reply and return the result.
- """
-
- resp = None
-
- def xendRequest(self, url, method, args=None):
- """Make a request to xend.
-
- @param url: xend request url
- @param method: http method: POST or GET
- @param args: request arguments (dict)
- """
- self.request = XendRequest(url, method, args)
- conn = httplib.HTTPConnection(url.location())
- if DEBUG: conn.set_debuglevel(1)
- conn.request(method, url.fullpath(), self.request.data, self.request.headers)
- resp = conn.getresponse()
- self.resp = resp
- val = self.handleStatus(resp.version, resp.status, resp.reason)
- if val is None:
- data = None
- else:
- data = resp.read()
- conn.close()
- val = self.handleResponse(data)
- return val
-
- def getHeader(self, key):
- return self.resp.getheader(key)
-
-
-class AsynchXendClient(http.HTTPClient):
- """A subclass of twisted's HTTPClient to deal with a connection to xend.
- Makes the request when connected, and delegates handling responses etc.
- to its protocol (usually an AsynchXendClientProtocol instance).
- """
- def __init__(self, protocol, request):
- self.protocol = protocol
- self.request = request
-
- def connectionMade(self):
- request = self.request
- url = self.request.url
- self.sendCommand(request.method, url.fullpath())
- self.sendHeader('Host', url.location())
- for (k, v) in request.headers.items():
- self.sendHeader(k, v)
- if request.data:
- self.sendHeader('Content-Length', len(request.data))
- self.endHeaders()
- if request.data:
- self.transport.write(request.data)
-
- def handleStatus(self, version, status, message):
- return self.protocol.handleStatus(version, status, message)
-
- def handleHeader(self, key, val):
- return self.protocol.handleHeader(key, val)
-
- def handleResponse(self, data):
- return self.protocol.handleResponse(data)
-
-class AsynchXendClientProtocol(XendClientProtocol):
- """An asynchronous xend client. Uses twisted to connect to xend
- and make the request. It does not block waiting for the result,
- but sets up a deferred that is called when the result becomes available.
-
- Uses AsynchXendClient to manage the connection.
- """
- def __init__(self):
- self.err = None
- self.headers = {}
-
- def xendRequest(self, url, method, args=None):
- """Make a request to xend. The returned deferred is called when
- the result is available.
-
- @param url: xend request url
- @param method: http method: POST or GET
- @param args: request arguments (dict)
- @return: deferred
- """
- request = XendRequest(url, method, args)
- self.deferred = Deferred()
- clientCreator = ClientCreator(reactor, AsynchXendClient, self, request)
- clientCreator.connectTCP(url.host, url.port)
- return self.deferred
-
- def callErrback(self, err):
- if not self.deferred.called:
- self.err = err
- self.deferred.errback(err)
- return err
-
- def callCallback(self, val):
- if not self.deferred.called:
- self.deferred.callback(val)
- return val
-
- def handleException(self, err):
- return self.callErrback(err)
-
- def handleHeader(self, key, val):
- self.headers[key.lower()] = val
-
- def getHeader(self, key):
- return self.headers.get(key.lower())
-
- def handleResponse(self, data):
- if self.err: return self.err
- val = XendClientProtocol.handleResponse(self, data)
- if isinstance(val, Exception):
- self.callErrback(val)
- else:
- self.callCallback(val)
- return val
-
class Xend:
"""Client interface to Xend.
"""
@@ -415,8 +179,12 @@ class Xend:
return self.xendPost(self.nodeurl(),
{'op' : 'reboot'})
- def xend_node_dmesg(self):
- return self.xendGet(self.nodeurl('dmesg'))
+ def xend_node_get_dmesg(self):
+ return self.xendGet(self.nodeurl('dmesg'))
+
+ def xend_node_clear_dmesg(self):
+ return self.xendPost(self.nodeurl('dmesg'),
+ {'op' : 'clear' } )
def xend_node_log(self):
return self.xendGet(self.nodeurl('log'))
@@ -431,11 +199,6 @@ class Xend:
{'op' : 'cpu_bvt_slice_set',
'ctx_allow' : ctx_allow })
- def xend_node_cpu_fbvt_slice_set(self, ctx_allow):
- return self.xendPost(self.nodeurl(),
- {'op' : 'cpu_fbvt_slice_set',
- 'ctx_allow' : ctx_allow })
-
def xend_domains(self):
return self.xendGet(self.domainurl())
@@ -465,10 +228,11 @@ class Xend:
return self.xendPost(self.domainurl(id),
{'op' : 'pause' })
- def xend_domain_shutdown(self, id, reason):
+ def xend_domain_shutdown(self, id, reason, key=0):
return self.xendPost(self.domainurl(id),
{'op' : 'shutdown',
- 'reason' : reason })
+ 'reason' : reason,
+ 'key' : key })
def xend_domain_destroy(self, id, reason):
return self.xendPost(self.domainurl(id),
@@ -480,10 +244,12 @@ class Xend:
{'op' : 'save',
'file' : filename })
- def xend_domain_migrate(self, id, dst):
+ def xend_domain_migrate(self, id, dst, live=0, resource=0):
return self.xendPost(self.domainurl(id),
- {'op' : 'migrate',
- 'destination': dst })
+ {'op' : 'migrate',
+ 'destination': dst,
+ 'live' : live,
+ 'resource' : resource })
def xend_domain_pincpu(self, id, cpu):
return self.xendPost(self.domainurl(id),
@@ -499,14 +265,6 @@ class Xend:
'warpl' : warpl,
'warpu' : warpu })
- def xend_domain_cpu_fbvt_set(self, id, mcuadv, warp, warpl, warpu):
- return self.xendPost(self.domainurl(id),
- {'op' : 'cpu_fbvt_set',
- 'mcuadv' : mcuadv,
- 'warp' : warp,
- 'warpl' : warpl,
- 'warpu' : warpu })
-
def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
return self.xendPost(self.domainurl(id),
{'op' : 'cpu_atropos_set',
@@ -547,7 +305,13 @@ class Xend:
return self.xendPost(self.domainurl(id),
{'op' : 'device_destroy',
'type' : type,
- 'index' : idx })
+ 'idx' : idx })
+
+ def xend_domain_device_configure(self, id, config, idx):
+ return self.xendPost(self.domainurl(id),
+ {'op' : 'device_configure',
+ 'idx' : idx,
+ 'config' : fileof(config) })
def xend_consoles(self):
return self.xendGet(self.consoleurl())
@@ -555,6 +319,10 @@ class Xend:
def xend_console(self, id):
return self.xendGet(self.consoleurl(id))
+ def xend_console_disconnect(self, id):
+ return self.xendPost(self.consoleurl(id),
+ {'op' : 'disconnect'})
+
def xend_vnets(self):
return self.xendGet(self.vneturl())
@@ -575,9 +343,34 @@ class Xend:
{'op' : 'inject',
'event' : fileof(sxpr) })
+ def xend_domain_mem_target_set(self, id, mem_target):
+ val = self.xendPost(self.domainurl(id),
+ {'op' : 'mem_target_set',
+ 'target' : mem_target })
+ return val
+
+def getAsynchXendClientProtocol():
+ """Load AsynchXendClientProtocol on demand to avoid the cost.
+ """
+ global AsynchXendClientProtocol
+ try:
+ AsynchXendClientProtocol
+ except:
+ from XendAsynchProtocol import AsynchXendClientProtocol
+ return AsynchXendClientProtocol
+
+def getAsynchServer():
+ """Load AsynchXendClientProtocol and create an asynch xend client.
+
+ @return asynch Xend
+ """
+ getAsynchXendClientProtocol()
+ return Xend(AsynchXendClientProtocol())
+
def xendmain(srv, asynch, fn, args):
if asynch:
- client = AsynchXendClientProtocol()
+ getAsynchXendClientProtocol()
+ client = AsynchXendClientProtocol()
else:
client = None
xend = Xend(srv=srv, client=client)
@@ -644,4 +437,3 @@ if __name__ == "__main__":
main(sys.argv)
else:
server = Xend()
- aserver = Xend( AsynchXendClientProtocol() )
diff --git a/tools/python/xen/xend/XendDmesg.py b/tools/python/xen/xend/XendDmesg.py
index 8d070ba45a..6201b721db 100644
--- a/tools/python/xen/xend/XendDmesg.py
+++ b/tools/python/xen/xend/XendDmesg.py
@@ -12,7 +12,9 @@ class XendDmesg:
def info(self):
return self.xc.readconsolering()
-
+
+ def clear(self):
+ self.xc.readconsolering(True)
def instance():
global inst
diff --git a/tools/python/xen/xend/XendDomain.py b/tools/python/xen/xend/XendDomain.py
index fbb6275b7a..1b01b43cfa 100644
--- a/tools/python/xen/xend/XendDomain.py
+++ b/tools/python/xen/xend/XendDomain.py
@@ -53,15 +53,14 @@ class XendDomain:
# Hack alert. Python does not support mutual imports, but XendDomainInfo
# needs access to the XendDomain instance to look up domains. Attempting
# to import XendDomain from XendDomainInfo causes unbounded recursion.
- # So we stuff the XendDomain instance (self) into XendDomainInfo's
- # namespace as 'xd'.
- XendDomainInfo.xd = self
+ # So we stuff the XendDomain instance (self) into xroot's components.
+ xroot.add_component("xen.xend.XendDomain", self)
# Table of domain info indexed by domain id.
self.db = XendDB.XendDB(self.dbpath)
self.domain_db = self.db.fetchall("")
- if xroot.get_rebooted():
- log.info('XendDomain> rebooted: removing all domain info')
- self.rm_all()
+ # XXXcl maybe check if there's only dom0 if we _really_ need
+ # to remove the db
+ # self.rm_all()
eserver.subscribe('xend.virq', self.onVirq)
self.initial_refresh()
@@ -198,9 +197,13 @@ class XendDomain:
"""
self.domain_by_id[info.id] = info
self.domain_db[info.id] = info.sxpr()
- self.domain_by_name[info.name] = info
+ for k, d in self.domain_by_name.items():
+ if k != d.name:
+ del self.domain_by_name[k]
+ if info.name:
+ self.domain_by_name[info.name] = info
self.sync_domain(info.id)
- if notify: eserver.inject('xend.domain.created', info.name)
+ if notify: eserver.inject('xend.domain.create', [info.name, info.id])
def _delete_domain(self, id, notify=1):
"""Remove a domain from the tables.
@@ -208,12 +211,13 @@ class XendDomain:
@param id: domain id
@param notify: send a domain died event if true
"""
+ for (k, info) in self.domain_by_name.items():
+ if info.id == id:
+ del self.domain_by_name[k]
if id in self.domain_by_id:
info = self.domain_by_id[id]
- if notify: eserver.inject('xend.domain.died', info.name)
- if info.name in self.domain_by_name:
- del self.domain_by_name[info.name]
del self.domain_by_id[id]
+ if notify: eserver.inject('xend.domain.died', [info.name, info.id])
if id in self.domain_db:
del self.domain_db[id]
self.db.delete(id)
@@ -232,17 +236,32 @@ class XendDomain:
not(d['running'] or d['paused'] or d['blocked']))
if dead:
casualties.append(d)
+ destroyed = 0
for d in casualties:
id = str(d['dom'])
- log.debug('XendDomain>reap> domain died id=%s', id)
+ dominfo = self.domain_by_id.get(id)
+ name = (dominfo and dominfo.name) or '??'
+ log.debug('XendDomain>reap> domain died name=%s id=%s', name, id)
if d['shutdown']:
reason = XendDomainInfo.shutdown_reason(d['shutdown_reason'])
log.debug('XendDomain>reap> shutdown id=%s reason=%s', id, reason)
+ if reason in ['suspend']:
+ if dominfo and dominfo.is_terminated():
+ log.debug('XendDomain>reap> Suspended domain died id=%s', id)
+ else:
+ eserver.inject('xend.domain.suspended', [name, id])
+ continue
if reason in ['poweroff', 'reboot']:
+ eserver.inject('xend.domain.exit', [name, id, reason])
self.domain_restart_schedule(id, reason)
+ else:
+ eserver.inject('xend.domain.exit', [name, id, 'crash'])
+ destroyed += 1
self.final_domain_destroy(id)
if self.domain_restarts_exist():
self.domain_restarts_schedule()
+ if destroyed:
+ self.refresh_schedule(delay=1)
def refresh(self):
"""Refresh domain list from Xen.
@@ -268,7 +287,7 @@ class XendDomain:
d.update(info)
else:
self._delete_domain(d.id)
- self.reap_schedule(1)
+ self.reap_schedule(delay=1)
def update_domain(self, id):
"""Update the saved info for a domain.
@@ -346,28 +365,29 @@ class XendDomain:
self._add_domain(dominfo)
return dominfo
log.info("Restarting domain: id=%s name=%s", dominfo.id, dominfo.name)
+ eserver.inject("xend.domain.restart",
+ [dominfo.name, dominfo.id, "begin"])
deferred = dominfo.restart()
deferred.addCallback(cbok)
return deferred
- def domain_configure(self, id, config):
+ def domain_configure(self, id, vmconfig):
"""Configure an existing domain. This is intended for internal
use by domain restore and migrate.
- @param id: domain id
- @param config: configuration
+ @param id: domain id
+ @param vmconfig: vm configuration
@return: deferred
"""
- dominfo = self.domain_get(id)
- if not dominfo:
- raise XendError("Invalid domain: " + str(id))
- log.debug('domain_configure> id=%s config=%s', id, str(config))
+ config = sxp.child_value(vmconfig, 'config')
+ dominfo = self.domain_lookup(id)
+ log.debug('domain_configure> id=%s config=%s', str(id), str(config))
if dominfo.config:
- raise XendError("Domain already configured: " + dominfo.name)
+ raise XendError("Domain already configured: " + dominfo.id)
def cbok(dominfo):
self._add_domain(dominfo)
return dominfo
- deferred = dominfo.construct(config)
+ deferred = dominfo.dom_construct(dominfo.dom, config)
deferred.addCallback(cbok)
return deferred
@@ -379,11 +399,15 @@ class XendDomain:
@return: deferred
"""
- def cbok(dominfo):
- self._add_domain(dominfo)
- return dominfo
- deferred = XendDomainInfo.vm_restore(src, progress=progress)
- deferred.addCallback(cbok)
+ if 0:
+ def cbok(dominfo):
+ self._add_domain(dominfo)
+ return dominfo
+ deferred = XendDomainInfo.vm_restore(src, progress=progress)
+ deferred.addCallback(cbok)
+ else:
+ xmigrate = XendMigrate.instance()
+ deferred = xmigrate.restore_begin(src)
return deferred
def domain_get(self, id):
@@ -405,10 +429,7 @@ class XendDomain:
def domain_exists(self, name):
name = str(name)
- if self.domain_by_name.get(name) or self.domain_by_id.get(name):
- return 1
- else:
- return 0
+ return self.domain_by_name.get(name) or self.domain_by_id.get(name)
def domain_unpause(self, id):
"""Unpause domain execution.
@@ -416,7 +437,7 @@ class XendDomain:
@param id: domain id
"""
dominfo = self.domain_lookup(id)
- eserver.inject('xend.domain.unpause', dominfo.name)
+ eserver.inject('xend.domain.unpause', [dominfo.name, dominfo.id])
try:
return xc.domain_unpause(dom=dominfo.dom)
except Exception, ex:
@@ -428,13 +449,13 @@ class XendDomain:
@param id: domain id
"""
dominfo = self.domain_lookup(id)
- eserver.inject('xend.domain.pause', dominfo.name)
+ eserver.inject('xend.domain.pause', [dominfo.name, dominfo.id])
try:
return xc.domain_pause(dom=dominfo.dom)
except Exception, ex:
raise XendError(str(ex))
- def domain_shutdown(self, id, reason='poweroff'):
+ def domain_shutdown(self, id, reason='poweroff', key=0):
"""Shutdown domain (nicely).
- poweroff: restart according to exit code and restart mode
- reboot: restart on exit
@@ -450,10 +471,10 @@ class XendDomain:
self.domain_restart_cancel(dominfo.id)
else:
self.domain_restart_schedule(dominfo.id, reason, force=1)
- eserver.inject('xend.domain.shutdown', [dominfo.name, reason])
+ eserver.inject('xend.domain.shutdown', [dominfo.name, dominfo.id, reason])
if reason == 'halt':
reason = 'poweroff'
- val = xend.domain_shutdown(dominfo.id, reason)
+ val = xend.domain_shutdown(dominfo.id, reason, key)
self.refresh_schedule()
return val
@@ -478,6 +499,8 @@ class XendDomain:
self.restarts_by_name[dominfo.name] = dominfo
self.restarts_by_id[dominfo.id] = dominfo
log.info('Scheduling restart for domain: name=%s id=%s', dominfo.name, dominfo.id)
+ eserver.inject("xend.domain.restart",
+ [dominfo.name, dominfo.id, "schedule"])
self.domain_restarts_schedule()
def domain_restart_cancel(self, id):
@@ -488,6 +511,8 @@ class XendDomain:
dominfo = self.restarts_by_id.get(id) or self.restarts_by_name.get(id)
if dominfo:
log.info('Cancelling restart for domain: name=%s id=%s', dominfo.name, dominfo.id)
+ eserver.inject("xend.domain.restart",
+ [dominfo.name, dominfo.id, "cancel"])
dominfo.restart_cancel()
del self.restarts_by_id[dominfo.id]
del self.restarts_by_name[dominfo.name]
@@ -506,16 +531,23 @@ class XendDomain:
try:
def cbok(dominfo):
log.info('Restarted domain name=%s id=%s', dominfo.name, dominfo.id)
+ eserver.inject("xend.domain.restart",
+ [dominfo.name, dominfo.id, "success"])
self.domain_unpause(dominfo.id)
def cberr(err):
log.exception("Delayed exception restarting domain: name=%s id=%s",
dominfo.name, dominfo.id)
+ eserver.inject("xend.domain.restart",
+ [dominfo.name, dominfo.id, "fail"])
+
deferred = self.domain_restart(dominfo)
deferred.addCallback(cbok)
deferred.addErrback(cberr)
except:
log.exception("Exception restarting domain: name=%s id=%s",
dominfo.name, dominfo.id)
+ eserver.inject("xend.domain.restart",
+ [dominfo.name, dominfo.id, "fail"])
if self.domain_restarts_exist():
# Run again later if any restarts remain.
self.refresh_schedule(delay=5)
@@ -530,7 +562,7 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
log.info('Destroying domain: name=%s', dominfo.name)
- eserver.inject('xend.domain.destroy', dominfo.name)
+ eserver.inject('xend.domain.destroy', [dominfo.name, dominfo.id])
if dominfo:
val = dominfo.destroy()
else:
@@ -553,7 +585,7 @@ class XendDomain:
self.refresh_schedule()
return val
- def domain_migrate(self, id, dst):
+ def domain_migrate(self, id, dst, live=0, resource=0):
"""Start domain migration.
@param id: domain id
@@ -561,11 +593,9 @@ class XendDomain:
"""
# Need a cancel too?
# Don't forget to cancel restart for it.
- print 'domain_migrate>', id, dst
dominfo = self.domain_lookup(id)
xmigrate = XendMigrate.instance()
- val = xmigrate.migrate_begin(dominfo.id, dst)
- print 'domain_migrate<', val
+ val = xmigrate.migrate_begin(dominfo, dst, live=live, resource=resource)
return val
def domain_save(self, id, dst, progress=0):
@@ -578,17 +608,17 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
xmigrate = XendMigrate.instance()
- return xmigrate.save_begin(dominfo.id, dst)
+ return xmigrate.save_begin(dominfo, dst)
- def domain_pincpu(self, dom, cpu):
+ def domain_pincpu(self, id, cpu):
"""Pin a domain to a cpu.
- @param dom: domain
+ @param id: domain
@param cpu: cpu number
"""
dominfo = self.domain_lookup(id)
try:
- return xc.domain_pincpu(itn(dominfo.id), cpu)
+ return xc.domain_pincpu(int(dominfo.id), cpu)
except Exception, ex:
raise XendError(str(ex))
@@ -612,25 +642,6 @@ class XendDomain:
except Exception, ex:
raise XendError(str(ex))
- def domain_cpu_fbvt_set(self, id, mcuadv, warp, warpl, warpu):
- """Set FBVT (Fair Borrowed Virtual Time) scheduler parameters for a domain.
- """
- dominfo = self.domain_lookup(id)
- try:
- return xc.fbvtsched_domain_set(dom=dominfo.dom, mcuadv=mcuadv,
- warp=warp, warpl=warpl, warpu=warpu)
- except Exception, ex:
- raise XendError(str(ex))
-
- def domain_cpu_fbvt_get(self, id):
- """Get FBVT (Fair Borrowed Virtual Time) scheduler parameters for a domain.
- """
- dominfo = self.domain_lookup(id)
- try:
- return xc.fbvtsched_domain_get(dominfo.dom)
- except Exception, ex:
- raise XendError(str(ex))
-
def domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
"""Set Atropos scheduler parameters for a domain.
"""
@@ -662,12 +673,27 @@ class XendDomain:
self.update_domain(dominfo.id)
return val
+ def domain_device_configure(self, id, devconfig, idx):
+ """Configure an existing device for a domain.
+
+ @param id: domain id
+ @param devconfig: device configuration
+ @param idx: device index
+ @return: updated device configuration
+ """
+ dominfo = self.domain_lookup(id)
+ self.refresh_schedule()
+ val = dominfo.device_configure(devconfig, idx)
+ self.update_domain(dominfo.id)
+ return val
+
+
def domain_device_destroy(self, id, type, idx):
"""Destroy a device.
@param id: domain id
- @param type: device type
@param idx: device index
+ @param type: device type
"""
dominfo = self.domain_lookup(id)
self.refresh_schedule()
@@ -684,7 +710,6 @@ class XendDomain:
"""
dominfo = self.domain_lookup(id)
devs = dominfo.get_devices(type)
- #return range(0, len(devs))
return devs
def domain_devtype_get(self, id, type, idx):
@@ -758,6 +783,10 @@ class XendDomain:
except Exception, ex:
raise XendError(str(ex))
+ def domain_mem_target_set(self, id, target):
+ return xend.domain_mem_target_set(id, target)
+
+
def instance():
"""Singleton constructor. Use this instead of the class constructor.
diff --git a/tools/python/xen/xend/XendDomainInfo.py b/tools/python/xen/xend/XendDomainInfo.py
index 45da2c250f..b2a8e6caea 100644
--- a/tools/python/xen/xend/XendDomainInfo.py
+++ b/tools/python/xen/xend/XendDomainInfo.py
@@ -16,23 +16,30 @@ import os
import time
from twisted.internet import defer
-#defer.Deferred.debug = 1
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
import xen.util.ip
from xen.util.ip import _readline, _readlines
+from xen.xend.server import channel
import sxp
import XendConsole
xendConsole = XendConsole.instance()
from XendLogging import log
+from XendRoot import get_component
import server.SrvDaemon
xend = server.SrvDaemon.instance()
from XendError import VmError
+"""The length of domain names that Xen can handle.
+The names stored in Xen itself are not used for much, and
+xend can handle domain names of any length.
+"""
+MAX_DOMAIN_NAME = 15
+
"""Flag for a block device backend domain."""
SIF_BLK_BE_DOMAIN = (1<<4)
@@ -68,6 +75,12 @@ STATE_RESTART_BOOTING = 'booting'
STATE_VM_OK = "ok"
STATE_VM_TERMINATED = "terminated"
+
+def domain_exists(name):
+ # See comment in XendDomain constructor.
+ xd = get_component('xen.xend.XendDomain')
+ return xd.domain_exists(name)
+
def shutdown_reason(code):
"""Get a shutdown reason from a code.
@@ -78,84 +91,10 @@ def shutdown_reason(code):
"""
return shutdown_reasons.get(code, "?")
-def blkdev_name_to_number(name):
- """Take the given textual block-device name (e.g., '/dev/sda1',
- 'hda') and return the device number used by the OS. """
-
- if not re.match( '^/dev/', name ):
- n = '/dev/' + name
-
- try:
- return os.stat(n).st_rdev
- except:
- pass
-
- # see if this is a hex device number
- if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
- return string.atoi(name,16)
-
- return None
-
-def lookup_raw_partn(name):
- """Take the given block-device name (e.g., '/dev/sda1', 'hda')
- and return a dictionary { device, start_sector,
- nr_sectors, type }
- device: Device number of the given partition
- start_sector: Index of first sector of the partition
- nr_sectors: Number of sectors comprising this partition
- type: 'Disk' or identifying name for partition type
- """
-
- n = blkdev_name_to_number(name)
- if n:
- return [ { 'device' : n,
- 'start_sector' : long(0),
- 'nr_sectors' : long(1L<<63),
- 'type' : 'Disk' } ]
- else:
- return None
-
-def lookup_disk_uname(uname):
- """Lookup a list of segments for a physical device.
- uname [string]: name of the device in the format \'phy:dev\' for a physical device
- returns [list of dicts]: list of extents that make up the named device
- """
- ( type, d_name ) = string.split( uname, ':' )
-
- if type == "phy":
- segments = lookup_raw_partn( d_name )
- else:
- segments = None
- return segments
-
-def make_disk(dom, uname, dev, mode, recreate=0):
- """Create a virtual disk device for a domain.
-
- @param dom: domain id
- @param uname: device to export
- @param dev: device name in domain
- @param mode: read/write mode
- @param recreate: recreate flag (after xend restart)
- @return: deferred
- """
- segments = lookup_disk_uname(uname)
- if not segments:
- raise VmError("vbd: Segments not found: uname=%s" % uname)
- if len(segments) > 1:
- raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname)
- segment = segments[0]
- vdev = blkdev_name_to_number(dev)
- ctrl = xend.blkif_create(dom, recreate=recreate)
-
- def fn(ctrl):
- return xend.blkif_dev_create(dom, vdev, mode, segment, recreate=recreate)
- ctrl.addCallback(fn)
- return ctrl
-
def vif_up(iplist):
"""send an unsolicited ARP reply for all non link-local IP addresses.
- iplist IP addresses
+ @param iplist: IP addresses
"""
IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
@@ -171,7 +110,7 @@ def vif_up(iplist):
def arping(ip, gw):
cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
- print cmd
+ log.debug(cmd)
os.system(cmd)
gateway = xen.util.ip.get_current_ipgw() or '255.255.255.255'
@@ -264,8 +203,9 @@ def vm_recreate(savedinfo, info):
"""
vm = XendDomainInfo()
vm.recreate = 1
+ vm.savedinfo = savedinfo
vm.setdom(info['dom'])
- vm.name = info['name']
+ #vm.name = info['name']
vm.memory = info['mem_kb']/1024
start_time = sxp.child_value(savedinfo, 'start_time')
if start_time is not None:
@@ -278,9 +218,10 @@ def vm_recreate(savedinfo, info):
if config:
d = vm.construct(config)
else:
- d = defer.Deferred()
- d.callback(vm)
+ vm.name = sxp.child_value(savedinfo, 'name', "Domain-%d" % info['dom'])
+ d = defer.succeed(vm)
vm.recreate = 0
+ vm.savedinfo = None
return d
def vm_restore(src, progress=0):
@@ -298,11 +239,15 @@ def vm_restore(src, progress=0):
dom = int(d['dom'])
if dom < 0:
raise VmError('restore failed')
- vmconfig = sxp.from_string(d['vmconfig'])
- vm.config = sxp.child_value(vmconfig, 'config')
- deferred = vm.dom_configure(dom)
+ try:
+ vmconfig = sxp.from_string(d['vmconfig'])
+ config = sxp.child_value(vmconfig, 'config')
+ except Exception, ex:
+ raise VmError('config error: ' + str(ex))
+ deferred = vm.dom_construct(dom, config)
def vifs_cb(val, vm):
vif_up(vm.ipaddrs)
+ return vm
deferred.addCallback(vifs_cb, vm)
return deferred
@@ -312,7 +257,7 @@ def dom_get(dom):
@param dom: domain id
@return: info or None
"""
- domlist = xc.domain_getinfo(dom=dom)
+ domlist = xc.domain_getinfo(dom, 1)
if domlist and dom == domlist[0]['dom']:
return domlist[0]
return None
@@ -326,21 +271,17 @@ def append_deferred(dlist, v):
if isinstance(v, defer.Deferred):
dlist.append(v)
-def _vm_configure1(val, vm):
- d = vm.create_devices()
- d.addCallback(_vm_configure2, vm)
- return d
+def dlist_err(val):
+ """Error callback suitable for a deferred list.
+ In a deferred list the error callback is called with with Failure((error, index)).
+ This callback extracts the error and returns it.
-def _vm_configure2(val, vm):
- d = vm.configure_fields()
- def cbok(results):
- return vm
- def cberr(err):
- vm.destroy()
- return err
- d.addCallback(cbok)
- d.addErrback(cberr)
- return d
+ @param val: Failure containing (error, index)
+ @type val: twisted.internet.failure.Failure
+ """
+
+ (error, index) = val.value
+ return error
class XendDomainInfo:
"""Virtual machine object."""
@@ -351,9 +292,11 @@ class XendDomainInfo:
def __init__(self):
self.recreate = 0
+ self.restore = 0
self.config = None
self.id = None
self.dom = None
+ self.cpu_weight = 1
self.start_time = None
self.name = None
self.memory = None
@@ -376,6 +319,10 @@ class XendDomainInfo:
self.restart_state = None
self.restart_time = None
self.console_port = None
+ self.savedinfo = None
+ self.image_handler = None
+ self.is_vmx = 0
+ self.vcpus = 1
def setdom(self, dom):
"""Set the domain id.
@@ -437,29 +384,48 @@ class XendDomainInfo:
sxpr.append(['restart_state', self.restart_state])
if self.restart_time:
sxpr.append(['restart_time', str(self.restart_time)])
+ devs = self.sxpr_devices()
+ if devs:
+ sxpr.append(devs)
if self.config:
sxpr.append(['config', self.config])
return sxpr
+ def sxpr_devices(self):
+ sxpr = ['devices']
+ for devs in self.devices.values():
+ for dev in devs:
+ if hasattr(dev, 'sxpr'):
+ sxpr.append(dev.sxpr())
+ return sxpr
+
def check_name(self, name):
"""Check if a vm name is valid. Valid names start with a non-digit
- and contain alphabetic characters, digits, or characters in '_-.'.
+ and contain alphabetic characters, digits, or characters in '_-.:/+'.
The same name cannot be used for more than one vm at the same time.
@param name: name
@raise: VMerror if invalid
"""
+ if self.recreate: return
if name is None or name == '':
raise VmError('missing vm name')
if name[0] in string.digits:
raise VmError('invalid vm name')
for c in name:
if c in string.digits: continue
- if c in '_-.': continue
+ if c in '_-.:/+': continue
if c in string.ascii_letters: continue
raise VmError('invalid vm name')
- # See comment in XendDomain constructor about 'xd'.
- if xd.domain_exists(name):
+ dominfo = domain_exists(name)
+ # When creating or rebooting, a domain with my name should not exist.
+ # When restoring, a domain with my name will exist, but it should have
+ # my domain id.
+ if not dominfo:
+ return
+ if dominfo.is_terminated():
+ return
+ if not self.dom or (dominfo.dom != self.dom):
raise VmError('vm name clash: ' + name)
def construct(self, config):
@@ -474,23 +440,28 @@ class XendDomainInfo:
try:
self.name = sxp.child_value(config, 'name')
self.check_name(self.name)
+ try:
+ self.cpu_weight = float(sxp.child_value(config, 'cpu_weight', '1'))
+ except:
+ raise VmError('invalid cpu weight')
self.memory = int(sxp.child_value(config, 'memory'))
if self.memory is None:
raise VmError('missing memory size')
+ cpu = sxp.child_value(config, 'cpu')
+ if self.recreate and self.dom and cpu is not None:
+ xc.domain_pincpu(self.dom, int(cpu))
+ try:
+ image = sxp.child_value(self.config, 'image')
+ self.vcpus = int(sxp.child_value(image, 'vcpus'))
+ except:
+ raise VmError('invalid vcpus value')
+ self.find_image_handler()
+ self.init_domain()
self.configure_console()
- self.configure_restart()
self.configure_backends()
- image = sxp.child_value(config, 'image')
- if image is None:
- raise VmError('missing image')
- image_name = sxp.name(image)
- if image_name is None:
- raise VmError('missing image name')
- image_handler = get_image_handler(image_name)
- if image_handler is None:
- raise VmError('unknown image type: ' + image_name)
- image_handler(self, image)
+ self.construct_image()
+ self.configure_restart()
deferred = self.configure()
def cberr(err):
self.destroy()
@@ -502,6 +473,30 @@ class XendDomainInfo:
raise
return deferred
+ def find_image_handler(self):
+ """Construct the boot image for the domain.
+
+ @return vm
+ """
+ image = sxp.child_value(self.config, 'image')
+ if image is None:
+ raise VmError('missing image')
+ image_name = sxp.name(image)
+ if image_name is None:
+ raise VmError('missing image name')
+ if image_name == "vmx":
+ self.is_vmx = 1
+ image_handler = get_image_handler(image_name)
+ if image_handler is None:
+ raise VmError('unknown image type: ' + image_name)
+ self.image_handler = image_handler
+ return self
+
+ def construct_image(self):
+ image = sxp.child_value(self.config, 'image')
+ self.image_handler(self, image)
+ return self
+
def config_devices(self, name):
"""Get a list of the 'device' nodes of a given type from the config.
@@ -556,6 +551,16 @@ class XendDomainInfo:
dl.append(dev)
self.devices[type] = dl
+ def remove_device(self, type, dev):
+ """Remove a device from a virtual machine.
+
+ @param type: device type
+ @param dev: device
+ """
+ dl = self.devices.get(type, [])
+ if dev in dl:
+ dl.remove(dev)
+
def get_devices(self, type):
"""Get a list of the devices of a given type.
@@ -590,6 +595,25 @@ class XendDomainInfo:
return d
return None
+ def get_device_savedinfo(self, type, index):
+ val = None
+ if self.savedinfo is None:
+ return val
+ index = str(index)
+ devinfo = sxp.child(self.savedinfo, 'devices')
+ if devinfo is None:
+ return val
+ for d in sxp.children(devinfo, type):
+ dindex = sxp.child_value(d, 'index')
+ if dindex is None: continue
+ if str(dindex) == index:
+ val = d
+ break
+ return val
+
+ def get_device_recreate(self, type, index):
+ return self.get_device_savedinfo(type, index) or self.recreate
+
def add_config(self, val):
"""Add configuration data to a virtual machine.
@@ -609,11 +633,7 @@ class XendDomainInfo:
devices have been released.
"""
if self.dom is None: return 0
- if self.console:
- if self.restart_pending():
- self.console.deregisterChannel()
- else:
- self.console.close()
+ self.destroy_console()
chan = xend.getDomChannel(self.dom)
if chan:
log.debug("Closing channel to domain %d", self.dom)
@@ -621,7 +641,15 @@ class XendDomainInfo:
try:
return xc.domain_destroy(dom=self.dom)
except Exception, err:
- log.exception("Domain destroy failed: ", self.name)
+ log.exception("Domain destroy failed: %s", self.name)
+
+ def destroy_console(self):
+ if self.console:
+ if self.restart_pending():
+ self.console.deregisterChannel()
+ else:
+ log.debug('Closing console, domain %s', self.id)
+ self.console.close()
def cleanup(self):
"""Cleanup vm resources: release devices.
@@ -639,7 +667,12 @@ class XendDomainInfo:
"""
self.release_vifs()
self.release_vbds()
+ self.release_usbifs()
+
self.devices = {}
+ self.device_index = {}
+ self.configs = []
+ self.ipaddrs = []
def release_vifs(self):
"""Release vm virtual network devices (vifs).
@@ -659,6 +692,15 @@ class XendDomainInfo:
log.debug("Destroying vbds for domain %d", self.dom)
ctrl.destroy()
+ def release_usbifs(self):
+ """Release vm virtual USB devices (usbifs).
+ """
+ if self.dom is None: return
+ ctrl = xend.usbif_get(self.dom)
+ if ctrl:
+ log.debug("Destroying usbifs for domain %d", self.dom)
+ ctrl.destroy()
+
def show(self):
"""Print virtual machine info.
"""
@@ -680,25 +722,40 @@ class XendDomainInfo:
def init_domain(self):
"""Initialize the domain memory.
"""
- if self.recreate: return
+ if self.recreate:
+ return
+ if self.start_time is None:
+ self.start_time = time.time()
+ if self.restore:
+ return
+ dom = self.dom or 0
memory = self.memory
name = self.name
- cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
- dom = self.dom or 0
- dom = xc.domain_create(dom= dom, mem_kb= memory * 1024, name= name, cpu= cpu)
+ # If the name is over the xen limit, use the end of it.
+ if len(name) > MAX_DOMAIN_NAME:
+ name = name[-MAX_DOMAIN_NAME:]
+ try:
+ cpu = int(sxp.child_value(self.config, 'cpu', '-1'))
+ except:
+ raise VmError('invalid cpu')
+ cpu_weight = self.cpu_weight
+ memory = memory * 1024 + self.pgtable_size(memory)
+ dom = xc.domain_create(dom= dom, mem_kb= memory,
+ cpu= cpu, cpu_weight= cpu_weight)
if dom <= 0:
raise VmError('Creating domain failed: name=%s memory=%d'
% (name, memory))
log.debug('init_domain> Created domain=%d name=%s memory=%d', dom, name, memory)
self.setdom(dom)
- if self.start_time is None:
- self.start_time = time.time()
-
- def build_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
+ def build_domain(self, ostype, kernel, ramdisk, cmdline, memmap):
"""Build the domain boot image.
"""
- if self.recreate: return
+ if self.recreate or self.restore: return
+ if not os.path.isfile(kernel):
+ raise VmError('Kernel image does not exist: %s' % kernel)
+ if ramdisk and not os.path.isfile(ramdisk):
+ raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
if len(cmdline) >= 256:
log.warning('kernel cmdline too long, domain %d', self.dom)
dom = self.dom
@@ -706,40 +763,64 @@ class XendDomainInfo:
flags = 0
if self.netif_backend: flags |= SIF_NET_BE_DOMAIN
if self.blkif_backend: flags |= SIF_BLK_BE_DOMAIN
- err = buildfn(dom = dom,
- image = kernel,
- control_evtchn = self.console.getRemotePort(),
- cmdline = cmdline,
- ramdisk = ramdisk,
- flags = flags)
+ if ostype == "vmx":
+ err = buildfn(dom = dom,
+ image = kernel,
+ control_evtchn = 0,
+ memsize = self.memory,
+ memmap = memmap,
+ cmdline = cmdline,
+ ramdisk = ramdisk,
+ flags = flags)
+ else:
+ log.warning('building dom with %d vcpus', self.vcpus)
+ err = buildfn(dom = dom,
+ image = kernel,
+ control_evtchn = self.console.getRemotePort(),
+ cmdline = cmdline,
+ ramdisk = ramdisk,
+ flags = flags,
+ vcpus = self.vcpus)
if err != 0:
raise VmError('Building domain failed: type=%s dom=%d err=%d'
% (ostype, dom, err))
- def create_domain(self, ostype, kernel, ramdisk, cmdline, vifs_n):
+ def create_domain(self, ostype, kernel, ramdisk, cmdline, memmap=''):
"""Create a domain. Builds the image but does not configure it.
@param ostype: OS type
@param kernel: kernel image
@param ramdisk: kernel ramdisk
@param cmdline: kernel commandline
- @param vifs_n: number of network interfaces
"""
- if not self.recreate:
- if not os.path.isfile(kernel):
- raise VmError('Kernel image does not exist: %s' % kernel)
- if ramdisk and not os.path.isfile(ramdisk):
- raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
- self.init_domain()
+
+ self.create_channel()
if self.console:
self.console.registerChannel()
else:
- self.console = xendConsole.console_create(self.dom, console_port=self.console_port)
- self.build_domain(ostype, kernel, ramdisk, cmdline, vifs_n)
+ self.console = xendConsole.console_create(
+ self.dom, console_port=self.console_port)
+ self.build_domain(ostype, kernel, ramdisk, cmdline, memmap)
self.image = kernel
self.ramdisk = ramdisk
self.cmdline = cmdline
+ def create_channel(self):
+ """Create the channel to the domain.
+ If saved info is available recreate the channel using the saved ports.
+
+ @return: channel
+ """
+ local = 0
+ remote = 1
+ if self.savedinfo:
+ consinfo = sxp.child(self.savedinfo, "console")
+ if consinfo:
+ local = int(sxp.child_value(consinfo, "local_port", 0))
+ remote = int(sxp.child_value(consinfo, "remote_port", 1))
+ return xend.createDomChannel(self.dom, local_port=local,
+ remote_port=remote)
+
def create_devices(self):
"""Create the devices for a vm.
@@ -762,6 +843,19 @@ class XendDomainInfo:
append_deferred(dlist, v)
index[dev_name] = dev_index + 1
deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
+ deferred.addErrback(dlist_err)
+ if self.is_vmx:
+ device_model = sxp.child_value(self.config, 'device_model')
+ device_config = sxp.child_value(self.config, 'device_config')
+ memory = sxp.child_value(self.config, "memory")
+ # Create an event channel
+ device_channel = channel.eventChannel(0, self.dom)
+ # Fork and exec device_model -f device_config <port>
+ os.system(device_model
+ + " -f %s" % device_config
+ + " -d %d" % self.dom
+ + " -p %d" % device_channel['port1']
+ + " -m %s" % memory)
return deferred
def device_create(self, dev_config):
@@ -777,9 +871,36 @@ class XendDomainInfo:
devs = self.get_devices(dev_name)
dev_index = len(devs)
self.config.append(['device', dev_config])
- d = dev_handler(self, dev_config, dev_index)
+ d = dev_handler(self, dev_config, dev_index, change=1)
+ def cbok(dev):
+ return dev.sxpr()
+ d.addCallback(cbok)
return d
+ def device_configure(self, dev_config, idx):
+ """Configure an existing device.
+
+ @param dev_config: device configuration
+ @param idx: device index
+ """
+ type = sxp.name(dev_config)
+ dev = self.get_device_by_index(type, idx)
+ if not dev:
+ raise VmError('invalid device: %s %s' % (type, idx))
+ new_config = dev.configure(dev_config, change=1)
+ devs = self.devices.get(type)
+ index = devs.index(dev)
+ # Patch new config into device configs.
+ dev_configs = self.config_devices(type)
+ old_config = dev_configs[index]
+ dev_configs[index] = new_config
+ # Patch new config into vm config.
+ new_full_config = ['device', new_config]
+ old_full_config = ['device', old_config]
+ old_index = self.config.index(old_full_config)
+ self.config[old_index] = new_full_config
+ return new_config
+
def device_destroy(self, type, idx):
"""Destroy a device.
@@ -794,7 +915,16 @@ class XendDomainInfo:
dev_config = self.config_device(type, index)
if dev_config:
self.config.remove(['device', dev_config])
- dev.destroy()
+ dev.destroy(change=1)
+ self.remove_device(type, dev)
+
+ def configure_memory(self):
+ """Configure vm memory limit.
+ """
+ maxmem = sxp.child_value(self.config, "maxmem")
+ if maxmem is None:
+ maxmem = self.memory
+ xc.domain_setmaxmem(self.dom, maxmem_kb = maxmem * 1024)
def configure_console(self):
"""Configure the vm console port.
@@ -847,7 +977,7 @@ class XendDomainInfo:
def restart_check(self):
"""Check if domain restart is OK.
- To prevent restart loops, raise an error it is
+ To prevent restart loops, raise an error if it is
less than MINIMUM_RESTART_TIME seconds since the last restart.
"""
tnow = time.time()
@@ -875,62 +1005,74 @@ class XendDomainInfo:
return d
def configure_backends(self):
- """Set configuration flags if the vm is a backend for netif of blkif.
+ """Set configuration flags if the vm is a backend for netif or blkif.
+ Configure the backends to use for vbd and vif if specified.
"""
for c in sxp.children(self.config, 'backend'):
- name = sxp.name(sxp.child0(c))
+ v = sxp.child0(c)
+ name = sxp.name(v)
if name == 'blkif':
self.blkif_backend = 1
elif name == 'netif':
self.netif_backend = 1
+ elif name == 'usbif':
+ self.usbif_backend = 1
else:
raise VmError('invalid backend type:' + str(name))
- def create_backends(self):
- """Setup the netif and blkif backends.
- """
- if self.blkif_backend:
- xend.blkif_set_control_domain(self.dom, recreate=self.recreate)
- if self.netif_backend:
- xend.netif_set_control_domain(self.dom, recreate=self.recreate)
-
def configure(self):
"""Configure a vm.
- vm virtual machine
- config configuration
+ @return: deferred - calls callback with vm
+ """
+ d = self.create_devices()
+ d.addCallback(lambda x: self.create_blkif())
+ d.addCallback(self._configure)
+ return d
+
+ def _configure(self, val):
+ d = self.configure_fields()
+ def cbok(results):
+ return self
+ def cberr(err):
+ self.destroy()
+ return err
+ d.addCallback(cbok)
+ d.addErrback(cberr)
+ return d
- returns Deferred - calls callback with vm
+ def create_blkif(self):
+ """Create the block device interface (blkif) for the vm.
+ The vm needs a blkif even if it doesn't have any disks
+ at creation time, for example when it uses NFS root.
+
+ @return: deferred
"""
- if self.blkif_backend:
- d = defer.Deferred()
- d.callback(self)
+ if self.get_devices("vbd") == []:
+ ctrl = xend.blkif_create(self.dom, recreate=self.recreate)
+ back = ctrl.getBackendInterface(0)
+ return back.connect(recreate=self.recreate)
else:
- d = xend.blkif_create(self.dom, recreate=self.recreate)
- d.addCallback(_vm_configure1, self)
- return d
+ return None
- def dom_configure(self, dom):
- """Configure a vm for an existing domain.
+ def dom_construct(self, dom, config):
+ """Construct a vm for an existing domain.
- @param dom: domain id
+ @param dom: domain id
+ @param config: domain configuration
@return: deferred
"""
d = dom_get(dom)
if not d:
raise VmError("Domain not found: %d" % dom)
try:
+ self.restore = 1
self.setdom(dom)
- self.name = d['name']
- self.memory = d['memory']/1024
- deferred = self.configure()
- def cberr(err):
- self.destroy()
- return err
- deferred.addErrback(cberr)
- except StandardError, ex:
- self.destroy()
- raise
+ #self.name = d['name']
+ self.memory = d['mem_kb']/1024
+ deferred = self.construct(config)
+ finally:
+ self.restore = 0
return deferred
def configure_fields(self):
@@ -950,8 +1092,21 @@ class XendDomainInfo:
log.warning("Unknown config field %s", field_name)
index[field_name] = field_index + 1
d = defer.DeferredList(dlist, fireOnOneErrback=1)
+ d.addErrback(dlist_err)
return d
+ def pgtable_size(self, memory):
+ """Return the size of memory needed for 1:1 page tables for physical
+ mode.
+
+ @param memory: size in MB
+ @return size in KB
+ """
+ if self.is_vmx:
+ # Logic x86-32 specific.
+ # 1 page for the PGD + 1 pte page for 4MB of memory (rounded)
+ return (1 + ((memory + 3) >> 2)) * 4
+ return 0
def vm_image_linux(vm, image):
"""Create a VM for a linux image.
@@ -963,7 +1118,7 @@ def vm_image_linux(vm, image):
"""
kernel = sxp.child_value(image, "kernel")
cmdline = ""
- ip = sxp.child_value(image, "ip", "dhcp")
+ ip = sxp.child_value(image, "ip", None)
if ip:
cmdline += " ip=" + ip
root = sxp.child_value(image, "root")
@@ -973,17 +1128,17 @@ def vm_image_linux(vm, image):
if args:
cmdline += " " + args
ramdisk = sxp.child_value(image, "ramdisk", '')
- vifs = vm.config_devices("vif")
- vm.create_domain("linux", kernel, ramdisk, cmdline, len(vifs))
+ vm.create_domain("linux", kernel, ramdisk, cmdline)
return vm
-def vm_image_netbsd(vm, image):
- """Create a VM for a bsd image.
+def vm_image_plan9(vm, image):
+ """Create a VM for a Plan 9 image.
- @param name: vm name
- @param memory: vm memory
- @param image: image config
- @return: vm
+ name vm name
+ memory vm memory
+ image image config
+
+ returns vm
"""
#todo: Same as for linux. Is that right? If so can unify them.
kernel = sxp.child_value(image, "kernel")
@@ -999,11 +1154,37 @@ def vm_image_netbsd(vm, image):
cmdline += " " + args
ramdisk = sxp.child_value(image, "ramdisk", '')
vifs = vm.config_devices("vif")
- vm.create_domain("netbsd", kernel, ramdisk, cmdline, len(vifs))
+ vm.create_domain("plan9", kernel, ramdisk, cmdline)
return vm
+
+def vm_image_vmx(vm, image):
+ """Create a VM for the VMX environment.
+ @param name: vm name
+ @param memory: vm memory
+ @param image: image config
+ @return: vm
+ """
+ kernel = sxp.child_value(image, "kernel")
+ cmdline = ""
+ ip = sxp.child_value(image, "ip", "dhcp")
+ if ip:
+ cmdline += " ip=" + ip
+ root = sxp.child_value(image, "root")
+ if root:
+ cmdline += " root=" + root
+ args = sxp.child_value(image, "args")
+ if args:
+ cmdline += " " + args
+ ramdisk = sxp.child_value(image, "ramdisk", '')
+ memmap = sxp.child_value(vm.config, "memmap", '')
+ memmap = sxp.parse(open(memmap))[0]
+ from xen.util.memmap import memmap_parse
+ memmap = memmap_parse(memmap)
+ vm.create_domain("vmx", kernel, ramdisk, cmdline, memmap)
+ return vm
-def vm_dev_vif(vm, val, index):
+def vm_dev_vif(vm, val, index, change=0):
"""Create a virtual network interface (vif).
@param vm: virtual machine
@@ -1011,22 +1192,40 @@ def vm_dev_vif(vm, val, index):
@param index: vif index
@return: deferred
"""
- if vm.netif_backend:
- raise VmError('vif: vif in netif backend domain')
vif = vm.next_device_index('vif')
vmac = sxp.child_value(val, "mac")
- xend.netif_create(vm.dom, recreate=vm.recreate)
+ ctrl = xend.netif_create(vm.dom, recreate=vm.recreate)
log.debug("Creating vif dom=%d vif=%d mac=%s", vm.dom, vif, str(vmac))
- defer = xend.netif_dev_create(vm.dom, vif, val, recreate=vm.recreate)
- def fn(id):
- dev = xend.netif_dev(vm.dom, vif)
+ recreate = vm.get_device_recreate('vif', index)
+ defer = ctrl.attachDevice(vif, val, recreate=recreate)
+ def cbok(dev):
dev.vifctl('up', vmname=vm.name)
+ dev.setIndex(index)
vm.add_device('vif', dev)
- return id
- defer.addCallback(fn)
+ if change:
+ dev.interfaceChanged()
+ return dev
+ defer.addCallback(cbok)
+ return defer
+
+def vm_dev_usb(vm, val, index):
+ """Attach the relevant physical ports to the domains' USB interface.
+
+ @param vm: virtual machine
+ @param val: USB interface config
+ @param index: USB interface index
+ @return: deferred
+ """
+ ctrl = xend.usbif_create(vm.dom, recreate=vm.recreate)
+ log.debug("Creating USB interface dom=%d", vm.dom)
+ defer = ctrl.attachDevice(val, recreate=vm.recreate)
+ def cbok(path):
+ vm.add_device('usb', val[1][1])
+ return path
+ defer.addCallback(cbok)
return defer
-def vm_dev_vbd(vm, val, index):
+def vm_dev_vbd(vm, val, index, change=0):
"""Create a virtual block device (vbd).
@param vm: virtual machine
@@ -1034,23 +1233,19 @@ def vm_dev_vbd(vm, val, index):
@param index: vbd index
@return: deferred
"""
- if vm.blkif_backend:
- raise VmError('vbd: vbd in blkif backend domain')
+ idx = vm.next_device_index('vbd')
uname = sxp.child_value(val, 'uname')
- if not uname:
- raise VmError('vbd: Missing uname')
- dev = sxp.child_value(val, 'dev')
- if not dev:
- raise VmError('vbd: Missing dev')
- mode = sxp.child_value(val, 'mode', 'r')
- log.debug("Creating vbd dom=%d uname=%s dev=%s", vm.dom, uname, dev)
- defer = make_disk(vm.dom, uname, dev, mode, vm.recreate)
- def fn(vbd):
- vbd.dev = dev
- vbd.uname = uname
- vm.add_device('vbd', vbd)
- return vbd
- defer.addCallback(fn)
+ log.debug("Creating vbd dom=%d uname=%s", vm.dom, uname)
+ ctrl = xend.blkif_create(vm.dom, recreate=vm.recreate)
+ recreate = vm.get_device_recreate('vbd', index)
+ defer = ctrl.attachDevice(idx, val, recreate=recreate)
+ def cbok(dev):
+ dev.setIndex(index)
+ vm.add_device('vbd', dev)
+ if change:
+ dev.interfaceChanged()
+ return dev
+ defer.addCallback(cbok)
return defer
def parse_pci(val):
@@ -1065,7 +1260,7 @@ def parse_pci(val):
v = val
return v
-def vm_dev_pci(vm, val, index):
+def vm_dev_pci(vm, val, index, change=0):
"""Add a pci device.
@param vm: virtual machine
@@ -1103,27 +1298,50 @@ def vm_field_ignore(vm, config, val, index):
@param vm: virtual machine
@param config: vm config
- @param val: vfr field
+ @param val: config field
@param index: field index
"""
pass
+def vm_field_maxmem(vm, config, val, index):
+ """Configure vm memory limit.
+
+ @param vm: virtual machine
+ @param config: vm config
+ @param val: config field
+ @param index: field index
+ """
+ maxmem = sxp.child0(val)
+ if maxmem is None:
+ maxmem = vm.memory
+ try:
+ maxmem = int(maxmem)
+ except:
+ raise VmError("invalid maxmem: " + str(maxmem))
+ xc.domain_setmaxmem(vm.dom, maxmem_kb = maxmem * 1024)
+
# Register image handlers.
add_image_handler('linux', vm_image_linux)
-add_image_handler('netbsd', vm_image_netbsd)
+add_image_handler('plan9', vm_image_plan9)
+add_image_handler('vmx', vm_image_vmx)
# Register device handlers.
add_device_handler('vif', vm_dev_vif)
add_device_handler('vbd', vm_dev_vbd)
add_device_handler('pci', vm_dev_pci)
+add_device_handler('usb', vm_dev_usb)
# Ignore the fields we already handle.
-add_config_handler('name', vm_field_ignore)
-add_config_handler('memory', vm_field_ignore)
-add_config_handler('cpu', vm_field_ignore)
-add_config_handler('console', vm_field_ignore)
-add_config_handler('image', vm_field_ignore)
-add_config_handler('device', vm_field_ignore)
-add_config_handler('backend', vm_field_ignore)
+add_config_handler('name', vm_field_ignore)
+add_config_handler('memory', vm_field_ignore)
+add_config_handler('cpu', vm_field_ignore)
+add_config_handler('cpu_weight', vm_field_ignore)
+add_config_handler('console', vm_field_ignore)
+add_config_handler('restart', vm_field_ignore)
+add_config_handler('image', vm_field_ignore)
+add_config_handler('device', vm_field_ignore)
+add_config_handler('backend', vm_field_ignore)
+add_config_handler('vcpus', vm_field_ignore)
# Register other config handlers.
+add_config_handler('maxmem', vm_field_maxmem)
diff --git a/tools/python/xen/xend/XendLogging.py b/tools/python/xen/xend/XendLogging.py
index 9be84b834c..9fc7cbb1c6 100644
--- a/tools/python/xen/xend/XendLogging.py
+++ b/tools/python/xen/xend/XendLogging.py
@@ -18,6 +18,9 @@ class XendLogging:
dateFormat = "%Y-%m-%d %H:%M:%S"
def __init__(self, filename, level=logging.INFO, maxBytes=None, backupCount=None):
+ """Initialise logging. Logs to 'filename' by default, but does not log to
+ stderr unless addLogStderr() is called.
+ """
self.setLevel(level)
if maxBytes:
self.maxBytes = maxBytes
@@ -25,7 +28,6 @@ class XendLogging:
self.backupCount = backupCount
self.initLogFile(filename)
self.initLogStderr()
- pass
def setLevel(self, level):
if isinstance(level, types.StringType):
@@ -40,6 +42,8 @@ class XendLogging:
return logging.getLogger("xend")
def initLogFile(self, filename):
+ """Create the file logger and add it.
+ """
self.logfile = RotatingFileHandler(filename,
mode='a',
maxBytes=self.maxBytes,
@@ -56,9 +60,18 @@ class XendLogging:
return self.logfilename
def initLogStderr(self):
+ """Create the stderr logger, but don't add it.
+ """
self.logstderr = StreamHandler()
self.logstderr.setFormatter(Formatter(self.logStderrFormat, self.dateFormat))
+
+ def addLogStderr(self):
+ """Add logging to stderr."""
self.getLogger().addHandler(self.logstderr)
+
+ def removeLogStderr(self):
+ """Remove logging to stderr."""
+ self.getLogger().removeHandler(self.logstderr)
def getLogStderr(self):
return self.logstderr
diff --git a/tools/python/xen/xend/XendMigrate.py b/tools/python/xen/xend/XendMigrate.py
index e5ce2de060..a1c261deb5 100644
--- a/tools/python/xen/xend/XendMigrate.py
+++ b/tools/python/xen/xend/XendMigrate.py
@@ -1,5 +1,7 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+import traceback
+
import errno
import sys
import socket
@@ -14,7 +16,9 @@ from twisted.internet.protocol import ClientFactory
import sxp
import XendDB
import EventServer; eserver = EventServer.instance()
-
+from XendError import XendError
+from XendLogging import log
+
"""The port for the migrate/save daemon xfrd."""
XFRD_PORT = 8002
@@ -41,9 +45,11 @@ class Xfrd(Protocol):
sxp.show(req, out=self.transport)
def loseConnection(self):
+ print 'Xfrd>loseConnection>'
self.transport.loseConnection()
def connectionLost(self, reason):
+ print 'Xfrd>connectionLost>', reason
self.xinfo.connectionLost(reason)
def dataReceived(self, data):
@@ -75,34 +81,50 @@ class XfrdClientFactory(ClientFactory):
def clientConnectionFailed(self, connector, reason):
print 'clientConnectionFailed>', 'connector=', connector, 'reason=', reason
+ self.xinfo.error(reason)
class XfrdInfo:
"""Abstract class for info about a session with xfrd.
Has subclasses for save and migrate.
"""
+ """Suspend timeout (seconds).
+ We set a timeout because suspending a domain can hang."""
+ timeout = 30
+
def __init__(self):
from xen.xend import XendDomain
self.xd = XendDomain.instance()
self.deferred = defer.Deferred()
+ self.suspended = {}
+ self.paused = {}
def vmconfig(self):
- print 'vmconfig>'
dominfo = self.xd.domain_get(self.src_dom)
- print 'vmconfig>', type(dominfo), dominfo
if dominfo:
val = sxp.to_string(dominfo.sxpr())
else:
val = None
- print 'vmconfig<', 'val=', type(val), val
return val
def error(self, err):
+ print 'Error>', err
self.state = 'error'
if not self.deferred.called:
+ print 'Error> calling errback'
self.deferred.errback(err)
def dispatch(self, xfrd, val):
+
+ def cbok(v):
+ if v is None: return
+ sxp.show(v, out=xfrd.transport)
+
+ def cberr(err):
+ v = ['xfr.err', errno.EINVAL]
+ sxp.show(v, out=xfrd.transport)
+ self.error(err)
+
op = sxp.name(val)
op = op.replace('.', '_')
if op.startswith('xfr_'):
@@ -110,8 +132,11 @@ class XfrdInfo:
else:
fn = self.unknown
val = fn(xfrd, val)
- if val is not None:
- sxp.show(val, out=xfrd.transport)
+ if isinstance(val, defer.Deferred):
+ val.addCallback(cbok)
+ val.addErrback(cberr)
+ else:
+ cbok(val)
def unknown(self, xfrd, val):
print 'unknown>', val
@@ -128,21 +153,31 @@ class XfrdInfo:
if not err: return
self.error(err);
xfrd.loseConnection()
- #try:
- # self.xd.domain_unpause(self.src_dom)
- #except:
- # print >>sys.stdout, "Error unpausing domain:", self.src_dom
return None
def xfr_progress(self, xfrd, val):
print 'xfr_progress>', val
return None
+ def xfr_vm_destroy(self, xfrd, val):
+ print 'xfr_vm_destroy>', val
+ try:
+ vmid = sxp.child0(val)
+ val = self.xd.domain_destroy(vmid)
+ if vmid in self.paused:
+ del self.paused[vmid]
+ if vmid in self.suspended:
+ del self.suspended[vmid]
+ except:
+ val = errno.EINVAL
+ return ['xfr.err', val]
+
def xfr_vm_pause(self, xfrd, val):
print 'xfr_vm_pause>', val
try:
vmid = sxp.child0(val)
val = self.xd.domain_pause(vmid)
+ self.paused[vmid] = 1
except:
val = errno.EINVAL
return ['xfr.err', val]
@@ -152,36 +187,99 @@ class XfrdInfo:
try:
vmid = sxp.child0(val)
val = self.xd.domain_unpause(vmid)
+ if vmid in self.paused:
+ del self.paused[vmid]
except:
val = errno.EINVAL
return ['xfr.err', val]
def xfr_vm_suspend(self, xfrd, val):
+ """Suspend a domain. Suspending takes time, so we return
+ a Deferred that is called when the suspend completes.
+ Suspending can hang, so we set a timeout and fail if it
+ takes too long.
+ """
print 'xfr_vm_suspend>', val
try:
vmid = sxp.child0(val)
+ d = defer.Deferred()
+ # Subscribe to 'suspended' events so we can tell when the
+ # suspend completes. Subscribe to 'died' events so we can tell if
+ # the domain died. Set a timeout and error handler so the subscriptions
+ # will be cleaned up if suspending hangs or there is an error.
+ def onSuspended(e, v):
+ print 'xfr_vm_suspend>onSuspended>', e, v
+ if v[1] != vmid: return
+ subscribe(on=0)
+ d.callback(v)
+
+ def onDied(e, v):
+ print 'xfr_vm_suspend>onDied>', e, v
+ if v[1] != vmid: return
+ d.errback(XendError('Domain died'))
+
+ def subscribe(on=1):
+ if on:
+ action = eserver.subscribe
+ else:
+ action = eserver.unsubscribe
+ action('xend.domain.suspended', onSuspended)
+ action('xend.domain.died', onDied)
+
+ def cberr(err):
+ print 'xfr_vm_suspend>cberr>', err
+ subscribe(on=0)
+ return err
+
+ subscribe()
val = self.xd.domain_shutdown(vmid, reason='suspend')
- except:
+ self.suspended[vmid] = 1
+ d.addErrback(cberr)
+ d.setTimeout(self.timeout)
+ return d
+ except Exception, err:
+ print 'xfr_vm_suspend> Exception', err
+ traceback.print_exc()
val = errno.EINVAL
return ['xfr.err', val]
+ def connectionLost(self, reason=None):
+ print 'XfrdInfo>connectionLost>', reason
+ for vmid in self.suspended:
+ try:
+ self.xd.domain_destroy(vmid)
+ except:
+ pass
+ for vmid in self.paused:
+ try:
+ self.xd.domain_unpause(vmid)
+ except:
+ pass
+
class XendMigrateInfo(XfrdInfo):
"""Representation of a migrate in-progress and its interaction with xfrd.
"""
- def __init__(self, xid, dom, host, port):
+ def __init__(self, xid, dominfo, host, port, live=0, resource=0):
XfrdInfo.__init__(self)
self.xid = xid
+ self.dominfo = dominfo
self.state = 'begin'
self.src_host = socket.gethostname()
- self.src_dom = dom
+ self.src_dom = dominfo.id
self.dst_host = host
self.dst_port = port
self.dst_dom = None
+ self.live = live
+ self.resource = resource
self.start = 0
def sxpr(self):
- sxpr = ['migrate', ['id', self.xid], ['state', self.state] ]
+ sxpr = ['migrate',
+ ['id', self.xid ],
+ ['state', self.state ],
+ ['live', self.live ],
+ ['resource', self.resource] ]
sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ]
sxpr.append(sxpr_src)
sxpr_dst = ['dst', ['host', self.dst_host] ]
@@ -195,36 +293,62 @@ class XendMigrateInfo(XfrdInfo):
if not vmconfig:
xfrd.loseConnection()
return
+ log.info('Migrate BEGIN: ' + str(self.sxpr()))
+ eserver.inject('xend.domain.migrate',
+ [ self.dominfo.name, self.dominfo.id,
+ "begin", self.sxpr() ])
xfrd.request(['xfr.migrate',
self.src_dom,
vmconfig,
self.dst_host,
- self.dst_port])
+ self.dst_port,
+ self.live,
+ self.resource ])
- def xfr_migrate_ok(self, val):
+## def xfr_vm_suspend(self, xfrd, val):
+## def cbok(val):
+## # Special case for localhost: destroy devices early.
+## if self.dst_host in ["localhost", "127.0.0.1"]:
+## self.dominfo.restart_cancel()
+## self.dominfo.cleanup()
+## self.dominfo.destroy_console()
+## return val
+
+## d = XfrdInfo.xfr_vm_suspend(self, xfrd, val)
+## d.addCallback(cbok)
+## return d
+
+ def xfr_migrate_ok(self, xfrd, val):
dom = int(sxp.child0(val))
self.state = 'ok'
self.dst_dom = dom
- self.xd_domain_destroy(self.src_dom)
+ self.xd.domain_destroy(self.src_dom)
if not self.deferred.called:
self.deferred.callback(self)
def connectionLost(self, reason=None):
+ print 'XfrdMigrateInfo>connectionLost>', reason
+ XfrdInfo.connectionLost(self, reason)
if self.state =='ok':
- eserver.inject('xend.migrate.ok', self.sxpr())
+ log.info('Migrate OK: ' + str(self.sxpr()))
else:
self.state = 'error'
- eserver.inject('xend.migrate.error', self.sxpr())
+ self.error(XendError("migrate failed"))
+ log.info('Migrate ERROR: ' + str(self.sxpr()))
+ eserver.inject('xend.domain.migrate',
+ [ self.dominfo.name, self.dominfo.id,
+ self.state, self.sxpr() ])
class XendSaveInfo(XfrdInfo):
"""Representation of a save in-progress and its interaction with xfrd.
"""
- def __init__(self, xid, dom, file):
+ def __init__(self, xid, dominfo, file):
XfrdInfo.__init__(self)
self.xid = xid
+ self.dominfo = dominfo
self.state = 'begin'
- self.src_dom = dom
+ self.src_dom = dominfo.id
self.file = file
self.start = 0
@@ -237,27 +361,66 @@ class XendSaveInfo(XfrdInfo):
return sxpr
def request(self, xfrd):
+ print '***request>', self.vmconfig()
vmconfig = self.vmconfig()
if not vmconfig:
xfrd.loseConnection()
return
+ print '***request> begin'
+ log.info('Save BEGIN: ' + str(self.sxpr()))
+ eserver.inject('xend.domain.save',
+ [self.dominfo.name, self.dominfo.id,
+ "begin", self.sxpr()])
xfrd.request(['xfr.save', self.src_dom, vmconfig, self.file ])
- def xfr_save_ok(self, val):
- dom = int(sxp.child0(val))
+ def xfr_save_ok(self, xfrd, val):
self.state = 'ok'
- self.xd_domain_destroy(self.src_dom)
+ self.xd.domain_destroy(self.src_dom)
if not self.deferred.called:
self.deferred.callback(self)
def connectionLost(self, reason=None):
+ print 'XfrdSaveInfo>connectionLost>', reason
+ XfrdInfo.connectionLost(self, reason)
if self.state =='ok':
- eserver.inject('xend.save.ok', self.sxpr())
+ log.info('Save OK: ' + str(self.sxpr()))
else:
self.state = 'error'
- eserver.inject('xend.save.error', self.sxpr())
+ self.error(XendError("save failed"))
+ log.info('Save ERROR: ' + str(self.sxpr()))
+ eserver.inject('xend.domain.save',
+ [ self.dominfo.name, self.dominfo.id,
+ self.state, self.sxpr() ])
+class XendRestoreInfo(XfrdInfo):
+ """Representation of a restore in-progress and its interaction with xfrd.
+ """
+ def __init__(self, xid, file):
+ XfrdInfo.__init__(self)
+ self.xid = xid
+ self.state = 'begin'
+ self.file = file
+
+ def sxpr(self):
+ sxpr = ['restore',
+ ['id', self.xid],
+ ['file', self.file] ]
+ return sxpr
+
+ def request(self, xfrd):
+ print '***request>', self.file
+ log.info('restore BEGIN: ' + str(self.sxpr()))
+ xfrd.request(['xfr.restore', self.file ])
+
+ def xfr_restore_ok(self, xfrd, val):
+ dom = int(sxp.child0(val))
+ dominfo = self.xd.domain_get(dom)
+ self.state = 'ok'
+ if not self.deferred.called:
+ self.deferred.callback(dominfo)
+
+
class XendMigrate:
"""External api for interaction with xfrd for migrate and save.
Singleton.
@@ -288,17 +451,19 @@ class XendMigrate:
def close(self):
pass
- def _add_session(self, xid, info):
+ def _add_session(self, info):
+ xid = info.xid
self.session[xid] = info
self.session_db[xid] = info.sxpr()
self.sync_session(xid)
- #eserver.inject('xend.migrate.begin', info.sxpr())
def _delete_session(self, xid):
- #eserver.inject('xend.migrate.end', xid)
- del self.session[xid]
- del self.session_db[xid]
- self.db.delete(xid)
+ print '***_delete_session>', xid
+ if xid in self.session:
+ del self.session[xid]
+ if xid in self.session_db:
+ del self.session_db[xid]
+ self.db.delete(xid)
def session_ls(self):
return self.session.keys()
@@ -310,37 +475,52 @@ class XendMigrate:
return self.session.get(xid)
def session_begin(self, info):
- self._add_session(info.xid, info)
- mcf = XfrdClientFactory(info)
- reactor.connectTCP('localhost', XFRD_PORT, mcf)
- return info
+ """Add the session to the table and start it.
+ Set up callbacks to remove the session from the table
+ when it finishes.
+
+ @param info: session
+ @return: deferred
+ """
+ def cbremove(val):
+ print '***cbremove>', val
+ self._delete_session(info.xid)
+ return val
+ self._add_session(info)
+ info.deferred.addCallback(cbremove)
+ info.deferred.addErrback(cbremove)
+ xcf = XfrdClientFactory(info)
+ reactor.connectTCP('localhost', XFRD_PORT, xcf)
+ return info.deferred
- def migrate_begin(self, dom, host, port=XFRD_PORT):
+ def migrate_begin(self, dominfo, host, port=XFRD_PORT, live=0, resource=0):
"""Begin to migrate a domain to another host.
- @param dom: domain
+ @param dominfo: domain info
@param host: destination host
@param port: destination port
@return: deferred
"""
- # Check dom for existence, not migrating already.
- # Subscribe to migrate notifications (for updating).
xid = self.nextid()
- info = XendMigrateInfo(xid, dom, host, port)
- self.session_begin(info)
- return info.deferred
+ info = XendMigrateInfo(xid, dominfo, host, port, live, resource)
+ return self.session_begin(info)
- def save_begin(self, dom, file):
+ def save_begin(self, dominfo, file):
"""Begin saving a domain to file.
- @param dom: domain
+ @param dominfo: domain info
@param file: destination file
@return: deferred
"""
xid = self.nextid()
- info = XendSaveInfo(xid, dom, file)
- self.session_begin(info)
- return info.deferred
+ info = XendSaveInfo(xid, dominfo, file)
+ return self.session_begin(info)
+
+ def restore_begin(self, file):
+ xid = self.nextid()
+ info = XendRestoreInfo(xid, file)
+ return self.session_begin(info)
+
def instance():
global inst
diff --git a/tools/python/xen/xend/XendNode.py b/tools/python/xen/xend/XendNode.py
index 4bc0b420e8..5fff0f62dd 100644
--- a/tools/python/xen/xend/XendNode.py
+++ b/tools/python/xen/xend/XendNode.py
@@ -30,12 +30,6 @@ class XendNode:
def cpu_bvt_slice_get(self):
return self.xc.bvtsched_global_get()
- def cpu_fbvt_slice_set(self, ctx_allow):
- return self.xc.bvtsched_global_set(ctx_allow=ctx_allow)
-
- def cpu_fbvt_slice_get(self):
- return self.xc.bvtsched_global_get()
-
def cpu_rrobin_slice_set(self, slice):
return self.xc.rrobin_global_set(slice=slice)
diff --git a/tools/python/xen/xend/XendProtocol.py b/tools/python/xen/xend/XendProtocol.py
new file mode 100644
index 0000000000..db4de7940f
--- /dev/null
+++ b/tools/python/xen/xend/XendProtocol.py
@@ -0,0 +1,156 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+import httplib
+import types
+
+from encode import *
+import sxp
+
+DEBUG = 0
+
+HTTP_OK = 200
+HTTP_CREATED = 201
+HTTP_ACCEPTED = 202
+HTTP_NO_CONTENT = 204
+
+class XendError(RuntimeError):
+ """Error class for 'expected errors' when talking to xend.
+ """
+ pass
+
+class XendRequest:
+ """A request to xend.
+ """
+
+ def __init__(self, url, method, args):
+ """Create a request. Sets up the headers, argument data, and the
+ url.
+
+ @param url: the url to request
+ @param method: request method, GET or POST
+ @param args: dict containing request args, if any
+ """
+ if url.proto != 'http':
+ raise ValueError('Invalid protocol: ' + url.proto)
+ (hdr, data) = encode_data(args)
+ if args and method == 'GET':
+ url.query = data
+ data = None
+ if method == "POST" and url.path.endswith('/'):
+ url.path = url.path[:-1]
+
+ self.headers = hdr
+ self.data = data
+ self.url = url
+ self.method = method
+
+class XendClientProtocol:
+ """Abstract class for xend clients.
+ """
+ def xendRequest(self, url, method, args=None):
+ """Make a request to xend.
+ Implement in a subclass.
+
+ @param url: xend request url
+ @param method: http method: POST or GET
+ @param args: request arguments (dict)
+ """
+ raise NotImplementedError()
+
+ def xendGet(self, url, args=None):
+ """Make a xend request using HTTP GET.
+ Requests using GET are usually 'safe' and may be repeated without
+ nasty side-effects.
+
+ @param url: xend request url
+ @param data: request arguments (dict)
+ """
+ return self.xendRequest(url, "GET", args)
+
+ def xendPost(self, url, args):
+ """Make a xend request using HTTP POST.
+ Requests using POST potentially cause side-effects, and should
+ not be repeated unless you really want to repeat the side
+ effect.
+
+ @param url: xend request url
+ @param args: request arguments (dict)
+ """
+ return self.xendRequest(url, "POST", args)
+
+ def handleStatus(self, version, status, message):
+ """Handle the status returned from the request.
+ """
+ status = int(status)
+ if status in [ HTTP_NO_CONTENT ]:
+ return None
+ if status not in [ HTTP_OK, HTTP_CREATED, HTTP_ACCEPTED ]:
+ return self.handleException(XendError(message))
+ return 'ok'
+
+ def handleResponse(self, data):
+ """Handle the data returned in response to the request.
+ """
+ if data is None: return None
+ type = self.getHeader('Content-Type')
+ if type != sxp.mime_type:
+ return data
+ try:
+ pin = sxp.Parser()
+ pin.input(data);
+ pin.input_eof()
+ val = pin.get_val()
+ except sxp.ParseError, err:
+ return self.handleException(err)
+ if isinstance(val, types.ListType) and sxp.name(val) == 'xend.err':
+ err = XendError(val[1])
+ return self.handleException(err)
+ return val
+
+ def handleException(self, err):
+ """Handle an exception during the request.
+ May be overridden in a subclass.
+ """
+ raise err
+
+ def getHeader(self, key):
+ """Get a header from the response.
+ Case is ignored in the key.
+
+ @param key: header key
+ @return: header
+ """
+ raise NotImplementedError()
+
+class SynchXendClientProtocol(XendClientProtocol):
+ """A synchronous xend client. This will make a request, wait for
+ the reply and return the result.
+ """
+
+ resp = None
+
+ def xendRequest(self, url, method, args=None):
+ """Make a request to xend.
+
+ @param url: xend request url
+ @param method: http method: POST or GET
+ @param args: request arguments (dict)
+ """
+ self.request = XendRequest(url, method, args)
+ conn = httplib.HTTPConnection(url.location())
+ if DEBUG: conn.set_debuglevel(1)
+ conn.request(method, url.fullpath(), self.request.data, self.request.headers)
+ resp = conn.getresponse()
+ self.resp = resp
+ val = self.handleStatus(resp.version, resp.status, resp.reason)
+ if val is None:
+ data = None
+ else:
+ data = resp.read()
+ conn.close()
+ val = self.handleResponse(data)
+ return val
+
+ def getHeader(self, key):
+ return self.resp.getheader(key)
+
diff --git a/tools/python/xen/xend/XendRoot.py b/tools/python/xen/xend/XendRoot.py
index 3a6392803c..dea3eeeb2f 100644
--- a/tools/python/xen/xend/XendRoot.py
+++ b/tools/python/xen/xend/XendRoot.py
@@ -16,26 +16,11 @@ eserver = EventServer.instance()
import sxp
-def reboots():
- """Get a list of system reboots from wtmp.
- """
- out = os.popen('last reboot', 'r')
- list = [ x.strip() for x in out if x.startswith('reboot') ]
- return list
-
-def last_reboot():
- """Get the last known system reboot.
- """
- l = reboots()
- return (l and l[-1]) or None
-
class XendRoot:
"""Root of the management classes."""
- lastboot_default = "/var/xen/lastboot"
-
"""Default path to the root of the database."""
- dbroot_default = "/var/xen/xend-db"
+ dbroot_default = "/var/lib/xen/xend-db"
"""Default path to the config file."""
config_default = "/etc/xen/xend-config.sxp"
@@ -44,75 +29,109 @@ class XendRoot:
config_var = "XEND_CONFIG"
"""Where network control scripts live."""
- network_script_dir = "/etc/xen"
+ network_script_dir = "/etc/xen/scripts"
+
+ """Where block control scripts live."""
+ block_script_dir = "/etc/xen/scripts"
logfile_default = "/var/log/xend.log"
loglevel_default = 'DEBUG'
+ components = {}
+
def __init__(self):
- self.rebooted = 0
- self.last_reboot = None
self.dbroot = None
self.config_path = None
self.config = None
+ self.logger = None
self.configure()
- self.check_lastboot()
eserver.subscribe('xend.*', self.event_handler)
#eserver.subscribe('xend.domain.created', self.event_handler)
#eserver.subscribe('xend.domain.died', self.event_handler)
+ def add_component(self, name, val):
+ """Add a xend component.
+
+ @param name: component name
+ @param val: component object
+ """
+ self.components[name] = val
+
+ def get_component(self, name):
+ """Get a xend component from its name.
+ This is used as a work-round for problems caused by mutually
+ recursive imports.
+
+ @param name: component name
+ @return: component object (or None)
+ """
+ return self.components.get(name)
+
def start(self):
- eserver.inject('xend.start', self.rebooted)
+ eserver.inject('xend.start', 0)
- def event_handler(self, event, val):
- print >> sys.stderr, "EVENT>", event, val
-
- def read_lastboot(self):
- try:
- val = file(self.lastboot, 'rb').readlines()[0]
- except StandardError, ex:
- print 'warning: Error reading', self.lastboot, ex
- val = None
- return val
+ def _format(self, msg, args):
+ if args:
+ return str(msg)
+ else:
+ return str(msg) % args
- def write_lastboot(self, val):
- if not val: return
- try:
- fdir = os.path.dirname(self.lastboot)
- if not os.path.isdir(fdir):
- os.makedirs(fdir)
- out = file(self.lastboot, 'wb+')
- out.write(val)
- out.close()
- except IOError, ex:
- print 'warning: Error writing', self.lastboot, ex
- pass
-
- def check_lastboot(self):
- """Check if there has been a system reboot since we saved lastboot.
+ def _log(self, mode, fmt, args):
+ """Logging function that uses the logger if it exists, otherwise
+ logs to stderr. We use this for XendRoot log messages because
+ they may be logged before the logger has been configured.
+ Other components can safely use the logger.
"""
- last_val = self.read_lastboot()
- this_val = last_reboot()
- if this_val == last_val:
- self.rebooted = 0
+ log = self.get_logger()
+ if mode not in ['warning', 'info', 'debug', 'error']:
+ mode = 'info'
+ level = mode.upper()
+ if log:
+ getattr(log, mode)(fmt, *args)
else:
- self.rebooted = 1
- self.write_lastboot(this_val)
- self.last_reboot = this_val
+ print >>stderr, "xend", "[%s]" % level, self._format(msg, args)
+
+ def logDebug(self, fmt, *args):
+ """Log a debug message.
- def get_last_reboot(self):
- return self.last_reboot
+ @param fmt: message format
+ @param args: arguments
+ """
+ self._log('info', fmt, args)
+
+ def logInfo(self, fmt, *args):
+ """Log an info message.
- def get_rebooted(self):
- return self.rebooted
+ @param fmt: message format
+ @param args: arguments
+ """
+ self._log('info', fmt, args)
+
+ def logWarning(self, fmt, *args):
+ """Log a warning message.
+
+ @param fmt: message format
+ @param args: arguments
+ """
+ self._log('warning', fmt, args)
+
+ def logError(self, fmt, *args):
+ """Log an error message.
+
+ @param fmt: message format
+ @param args: arguments
+ """
+ self._log('error', fmt, args)
+
+ def event_handler(self, event, val):
+ self.logInfo("EVENT> %s %s", str(event), str(val))
def configure(self):
- print 'XendRoot>configure>'
self.set_config()
self.configure_logger()
self.dbroot = self.get_config_value("dbroot", self.dbroot_default)
- self.lastboot = self.get_config_value("lastboot", self.lastboot_default)
+
def configure_logger(self):
logfile = self.get_config_value("logfile", self.logfile_default)
@@ -120,9 +139,13 @@ class XendRoot:
self.logging = XendLogging(logfile, level=loglevel)
def get_logging(self):
+ """Get the XendLogging instance.
+ """
return self.logging
def get_logger(self):
+ """Get the logger.
+ """
return self.logging.getLogger()
def get_dbroot(self):
@@ -136,9 +159,7 @@ class XendRoot:
The config file is a sequence of sxp forms.
"""
self.config_path = os.getenv(self.config_var, self.config_default)
- print 'XendRoot>set_config> config_path=', self.config_path
if os.path.exists(self.config_path):
- print 'XendRoot>set_config> loading'
fin = file(self.config_path, 'rb')
try:
config = sxp.parse(fin)
@@ -147,9 +168,7 @@ class XendRoot:
finally:
fin.close()
else:
- print 'XendRoot>set_config> not found'
self.config = ['xend-config']
- print 'XendRoot> config=', self.config
def get_config(self, name=None):
"""Get the configuration element with the given name, or
@@ -179,6 +198,9 @@ class XendRoot:
def get_xend_address(self):
return self.get_config_value('xend-address', '')
+ def get_block_script(self, type):
+ return self.get_config_value('block-%s' % type, '')
+
def get_network_script(self):
return self.get_config_value('network-script', 'network')
@@ -193,6 +215,9 @@ class XendRoot:
return v in ['yes', '1', 'on']
def instance():
+ """Get an instance of XendRoot.
+ Use this instead of the constructor.
+ """
global inst
try:
inst
@@ -201,4 +226,24 @@ def instance():
return inst
def logger():
+ """Get the logger.
+ """
return instance().get_logger()
+
+def add_component(name, val):
+ """Register a component with XendRoot.
+ This is used to work-round import cycles.
+
+ @param name: component name
+ @param val: component value (often a module)
+ """
+ return instance().add_component(name, val)
+
+def get_component(name):
+ """Get a component.
+ This is used to work-round import cycles.
+
+ @param name: component name
+ @return component or None
+ """
+ return instance().get_component(name)
diff --git a/tools/python/xen/xend/XendVnet.py b/tools/python/xen/xend/XendVnet.py
index 83506b6c6c..d95fd204aa 100644
--- a/tools/python/xen/xend/XendVnet.py
+++ b/tools/python/xen/xend/XendVnet.py
@@ -53,8 +53,11 @@ class XendVnetInfo:
return vnet_cmd(['vnet.del', self.id])
def vifctl(self, op, vif, vmac):
- fn = self.vifctl_ops[op]
- return vnet_cmd([fn, ['vif', vif], ['vmac', vmac]])
+ try:
+ fn = self.vifctl_ops[op]
+ return vnet_cmd([fn, ['vnet', self.id], ['vif', vif], ['vmac', vmac]])
+ except XendError:
+ log.warning("vifctl failed: op=%s vif=%s mac=%s", op, vif, vmac)
class XendVnet:
"""Index of all vnets. Singleton.
@@ -72,8 +75,8 @@ class XendVnet:
self.vnet[info.id] = info
try:
info.configure()
- except:
- log.exception("Error configuring vnet")
+ except XendError, ex:
+ log.warning("Failed to configure vnet %s: %s", str(info.id), str(ex))
def vnet_of_bridge(self, bridge):
"""Get the vnet for a bridge (if any).
diff --git a/tools/python/xen/xend/encode.py b/tools/python/xen/xend/encode.py
index 38c9351db7..48815defa9 100644
--- a/tools/python/xen/xend/encode.py
+++ b/tools/python/xen/xend/encode.py
@@ -14,6 +14,8 @@ import httplib
import random
import md5
+from xen.util.ip import _readline, _readlines
+
# Extract from HTML4 spec.
## The following example illustrates "multipart/form-data"
## encoding. Suppose we have the following form:
@@ -122,7 +124,7 @@ def encode_multipart(d):
out.write('"\r\n')
out.write('Content-Type: application/octet-stream\r\n')
out.write('\r\n')
- for l in v.readlines():
+ for l in _readlines(v):
out.write(l)
else:
out.write('Content-Disposition: form-data; name="')
diff --git a/tools/python/xen/xend/server/SrvBase.py b/tools/python/xen/xend/server/SrvBase.py
index 6bc32b42bc..b32d102273 100644
--- a/tools/python/xen/xend/server/SrvBase.py
+++ b/tools/python/xen/xend/server/SrvBase.py
@@ -8,17 +8,18 @@ import types
import StringIO
from twisted.internet import defer
-#defer.Deferred.debug = 1
from twisted.internet import reactor
from twisted.protocols import http
from twisted.web import error
from twisted.web import resource
from twisted.web import server
+from twisted.python.failure import Failure
from xen.xend import sxp
from xen.xend import PrettyPrint
from xen.xend.Args import ArgError
from xen.xend.XendError import XendError
+from xen.xend.XendLogging import log
def uri_pathlist(p):
"""Split a path into a list.
@@ -105,17 +106,17 @@ class SrvBase(resource.Resource):
try:
val = op_method(op, req)
except Exception, err:
- return self._perform_err(err, req)
+ return self._perform_err(err, op, req)
if isinstance(val, defer.Deferred):
- val.addCallback(self._perform_cb, req, dfr=1)
- val.addErrback(self._perform_err, req, dfr=1)
+ val.addCallback(self._perform_cb, op, req, dfr=1)
+ val.addErrback(self._perform_err, op, req, dfr=1)
return server.NOT_DONE_YET
else:
- self._perform_cb(val, req, 0)
+ self._perform_cb(val, op, req, dfr=0)
return ''
- def _perform_cb(self, val, req, dfr):
+ def _perform_cb(self, val, op, req, dfr=0):
"""Callback to complete the request.
May be called from a Deferred.
@@ -141,7 +142,7 @@ class SrvBase(resource.Resource):
if dfr:
req.finish()
- def _perform_err(self, err, req, dfr=0):
+ def _perform_err(self, err, op, req, dfr=0):
"""Error callback to complete a request.
May be called from a Deferred.
@@ -149,13 +150,16 @@ class SrvBase(resource.Resource):
@param req: request causing the error
@param dfr: deferred flag
"""
- if not (isinstance(err, ArgError) or
- isinstance(err, sxp.ParseError) or
- isinstance(err, XendError)):
+ if isinstance(err, Failure):
+ err = err.getErrorMessage()
+ elif not (isinstance(err, ArgError) or
+ isinstance(err, sxp.ParseError) or
+ isinstance(err, XendError)):
if dfr:
return err
else:
raise
+ log.exception("op=%s: %s", op, str(err))
if self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
sxp.show(['xend.err', str(err)], out=req)
diff --git a/tools/python/xen/xend/server/SrvDaemon.py b/tools/python/xen/xend/server/SrvDaemon.py
index aaeef6d54c..0c2beafe7f 100644
--- a/tools/python/xen/xend/server/SrvDaemon.py
+++ b/tools/python/xen/xend/server/SrvDaemon.py
@@ -23,7 +23,6 @@ from twisted.internet import reactor
from twisted.internet import protocol
from twisted.internet import abstract
from twisted.internet import defer
-#defer.Deferred.debug = 1
from xen.lowlevel import xu
@@ -36,136 +35,19 @@ from xen.xend.server import SrvServer
from xen.xend import XendRoot
from xen.xend.XendLogging import log
+from xen.util.ip import _readline, _readlines
+
import channel
import blkif
import netif
+import usbif
import console
import domain
from params import *
+DAEMONIZE = 1
DEBUG = 1
-class MgmtProtocol(protocol.DatagramProtocol):
- """Handler for the management socket (unix-domain).
- """
-
- def __init__(self, daemon):
- #protocol.DatagramProtocol.__init__(self)
- self.daemon = daemon
-
- def write(self, data, addr):
- return self.transport.write(data, addr)
-
- def datagramReceived(self, data, addr):
- if DEBUG: print 'datagramReceived> addr=', addr, 'data=', data
- io = StringIO.StringIO(data)
- try:
- vals = sxp.parse(io)
- res = self.dispatch(vals[0])
- self.send_result(addr, res)
- except SystemExit:
- raise
- except:
- if DEBUG:
- raise
- else:
- self.send_error(addr)
-
- def send_reply(self, addr, sxpr):
- io = StringIO.StringIO()
- sxp.show(sxpr, out=io)
- io.seek(0)
- self.write(io.getvalue(), addr)
-
- def send_result(self, addr, res):
-
- def fn(res, self=self, addr=addr):
- self.send_reply(addr, ['ok', res])
-
- if isinstance(res, defer.Deferred):
- res.addCallback(fn)
- else:
- fn(res)
-
- def send_error(self, addr):
- (extype, exval) = sys.exc_info()[:2]
- self.send_reply(addr, ['err',
- ['type', str(extype) ],
- ['value', str(exval) ] ] )
-
- def opname(self, name):
- """Get the name of the method for an operation.
- """
- return 'op_' + name.replace('.', '_')
-
- def operror(self, name, v):
- """Default operation handler - signals an error.
- """
- raise NotImplementedError('Invalid operation: ' +name)
-
- def dispatch(self, req):
- """Dispatch a request to its handler.
- """
- op_name = sxp.name(req)
- op_method_name = self.opname(op_name)
- op_method = getattr(self, op_method_name, self.operror)
- return op_method(op_name, req)
-
- def op_console_create(self, name, req):
- """Create a new control interface - console for a domain.
- """
- print name, req
- dom = sxp.child_value(req, 'domain')
- if not dom: raise XendError('Missing domain')
- dom = int(dom)
- console_port = sxp.child_value(req, 'console_port')
- if console_port:
- console_port = int(console_port)
- resp = self.daemon.console_create(dom, console_port).sxpr()
- print name, resp
- return resp
-
- def op_consoles(self, name, req):
- """Get a list of the consoles.
- """
- return self.daemon.consoles()
-
- def op_console_disconnect(self, name, req):
- id = sxp.child_value(req, 'id')
- if not id:
- raise XendError('Missing console id')
- id = int(id)
- console = self.daemon.get_console(id)
- if not console:
- raise XendError('Invalid console id')
- if console.conn:
- console.conn.loseConnection()
- return ['ok']
-
- def op_blkifs(self, name, req):
- pass
-
- def op_blkif_devs(self, name, req):
- pass
-
- def op_blkif_create(self, name, req):
- pass
-
- def op_blkif_dev_create(self, name, req):
- pass
-
- def op_netifs(self, name, req):
- pass
-
- def op_netif_devs(self, name, req):
- pass
-
- def op_netif_create(self, name, req):
- pass
-
- def op_netif_dev_create(self, name, req):
- pass
-
class NotifierProtocol(protocol.Protocol):
"""Asynchronous handler for i/o on the notifier (event channel).
"""
@@ -237,25 +119,22 @@ class NotifierPort(abstract.FileDescriptor):
if hasattr(self, 'protocol'):
self.protocol.doStop()
self.connected = 0
- #self.notifier.close() # Not implemented.
- os.close(self.fileno())
- del self.notifier
+ #self.notifier.close() # (this said:) Not implemented.
+ #os.close(self.fileno()) # But yes it is...
+ del self.notifier # ...as _dealloc!
if hasattr(self, 'd'):
self.d.callback(None)
del self.d
def doRead(self):
- #print 'NotifierPort>doRead>', self
count = 0
while 1:
- #print 'NotifierPort>doRead>', count
notification = self.notifier.read()
if not notification:
break
self.protocol.notificationReceived(notification)
self.notifier.unmask(notification)
count += 1
- #print 'NotifierPort>doRead<'
class EventProtocol(protocol.Protocol):
"""Asynchronous handler for a connected event socket.
@@ -334,7 +213,7 @@ class EventProtocol(protocol.Protocol):
self.events = events
def queue_event(self, name, v):
- # Despite the name we dont' queue the event here.
+ # Despite the name we don't queue the event here.
# We send it because the transport will queue it.
self.send_event([name, v])
@@ -374,6 +253,7 @@ class EventProtocol(protocol.Protocol):
id = sxp.child_value(req, 'id')
if not id:
raise XendError('Missing console id')
+ id = int(id)
self.daemon.console_disconnect(id)
return ['ok']
@@ -382,6 +262,7 @@ class EventProtocol(protocol.Protocol):
val += self.daemon.consoles()
val += self.daemon.blkifs()
val += self.daemon.netifs()
+ val += self.daemon.usbifs()
return val
def op_sys_subscribe(self, name, v):
@@ -396,11 +277,27 @@ class EventProtocol(protocol.Protocol):
eserver.inject(sxp.name(event), event)
return ['ok']
- def op_traceon(self, name, v):
- self.daemon.tracing(1)
+ def op_trace(self, name, v):
+ mode = (v[1] == 'on')
+ self.daemon.tracing(mode)
+
+ def op_log_stderr(self, name, v):
+ mode = v[1]
+ logging = XendRoot.instance().get_logging()
+ if mode == 'on':
+ logging.addLogStderr()
+ else:
+ logging.removeLogStderr()
- def op_traceoff(self, name, v):
- self.daemon.tracing(0)
+ def op_debug_msg(self, name, v):
+ mode = v[1]
+ import messages
+ messages.DEBUG = (mode == 'on')
+
+ def op_debug_controller(self, name, v):
+ mode = v[1]
+ import controller
+ controller.DEBUG = (mode == 'on')
class EventFactory(protocol.Factory):
@@ -464,24 +361,80 @@ class Daemon:
err = 1
print "Daemon already running: ", pids
return err
-
+
+ def read_pid(self, pidfile):
+ """Read process id from a file.
+
+ @param pidfile: file to read
+ @return pid or 0
+ """
+ pid = 0
+ if os.path.isfile(pidfile) and os.path.getsize(pidfile):
+ try:
+ pid = open(pidfile, 'r').read()
+ pid = int(pid)
+ except:
+ pid = 0
+ return pid
+
+ def find_process(self, pid, name):
+ """Search for a process.
+
+ @param pid: process id
+ @param name: process name
+ @return: pid if found, 0 otherwise
+ """
+ running = 0
+ if pid:
+ lines = _readlines(os.popen('ps %d 2>/dev/null' % pid))
+ exp = '^ *%d.+%s' % (pid, name)
+ for line in lines:
+ if re.search(exp, line):
+ running = pid
+ break
+ return running
+
+ def cleanup_process(self, pidfile, name, kill):
+ """Clean up the pidfile for a process.
+ If a running process is found, kills it if 'kill' is true.
+
+ @param pidfile: pid file
+ @param name: process name
+ @param kill: whether to kill the process
+ @return running process id or 0
+ """
+ running = 0
+ pid = self.read_pid(pidfile)
+ if self.find_process(pid, name):
+ if kill:
+ os.kill(pid, 1)
+ else:
+ running = pid
+ if running == 0 and os.path.isfile(pidfile):
+ os.remove(pidfile)
+ return running
+
+ def cleanup_xend(self, kill=False):
+ return self.cleanup_process(XEND_PID_FILE, "xend", kill)
+
+ def cleanup_xfrd(self, kill=False):
+ return self.cleanup_process(XFRD_PID_FILE, "xfrd", kill)
+
def cleanup(self, kill=False):
- # No cleanup to do if PID_FILE is empty.
- if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE):
+ self.cleanup_xend(kill=kill)
+ self.cleanup_xfrd(kill=kill)
+
+ def status(self):
+ """Returns the status of the xend and xfrd daemons.
+ The return value is defined by the LSB:
+ 0 Running
+ 3 Not running
+ """
+ if (self.cleanup_process(XEND_PID_FILE, "xend", False) == 0 or
+ self.cleanup_process(XFRD_PID_FILE, "xfrd", False) == 0):
+ return 3
+ else:
return 0
- # Read the pid of the previous invocation and search active process list.
- pid = open(PID_FILE, 'r').read()
- lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
- for line in lines:
- if re.search('^ *' + pid + '.+xend', line):
- if not kill:
- print "Daemon is already running (pid %d)" % int(pid)
- return 1
- # Old daemon is still active: terminate it.
- os.kill(int(pid), 1)
- # Delete the stale PID_FILE.
- os.remove(PID_FILE)
- return 0
def install_child_reaper(self):
#signal.signal(signal.SIGCHLD, self.onSIGCHLD)
@@ -493,42 +446,96 @@ class Daemon:
while code > 0:
code = os.waitpid(-1, os.WNOHANG)
- def start(self, trace=0):
- if self.cleanup(kill=False):
- return 1
-
- # Detach from TTY.
- if not DEBUG:
- os.setsid()
-
- if self.set_user():
- return 1
+ def fork_pid(self, pidfile):
+ """Fork and write the pid of the child to 'pidfile'.
- self.install_child_reaper()
-
- # Fork -- parent writes PID_FILE and exits.
+ @param pidfile: pid file
+ @return: pid of child in parent, 0 in child
+ """
pid = os.fork()
if pid:
# Parent
- pidfile = open(PID_FILE, 'w')
+ pidfile = open(pidfile, 'w')
pidfile.write(str(pid))
pidfile.close()
+ return pid
+
+ def start_xfrd(self):
+ """Fork and exec xfrd, writing its pid to XFRD_PID_FILE.
+ """
+ if self.fork_pid(XFRD_PID_FILE):
+ # Parent
+ pass
+ else:
+ # Child
+ os.execl("/usr/sbin/xfrd", "xfrd")
+
+ def daemonize(self):
+ if not DAEMONIZE: return
+ # Detach from TTY.
+ os.setsid()
+
+ # Detach from standard file descriptors.
+ # I do this at the file-descriptor level: the overlying Python file
+ # objects also use fd's 0, 1 and 2.
+ os.close(0)
+ os.close(1)
+ os.close(2)
+ if DEBUG:
+ os.open('/dev/null', os.O_RDONLY)
+ # XXX KAF: Why doesn't this capture output from C extensions that
+ # fprintf(stdout) or fprintf(stderr) ??
+ os.open('/var/log/xend-debug.log', os.O_WRONLY|os.O_CREAT)
+ os.dup(1)
+ else:
+ os.open('/dev/null', os.O_RDWR)
+ os.dup(0)
+ os.open('/var/log/xend-debug.log', os.O_WRONLY|os.O_CREAT)
+
+
+ def start(self, trace=0):
+ """Attempts to start the daemons.
+ The return value is defined by the LSB:
+ 0 Success
+ 4 Insufficient privileges
+ """
+ xend_pid = self.cleanup_xend()
+ xfrd_pid = self.cleanup_xfrd()
+
+
+ self.daemonize()
+
+ if self.set_user():
+ return 4
+ os.chdir("/")
+
+ if xfrd_pid == 0:
+ self.start_xfrd()
+ if xend_pid > 0:
+ # Trying to run an already-running service is a success.
return 0
- # Child
- self.tracing(trace)
- self.run()
+
+ self.install_child_reaper()
+
+ if self.fork_pid(XEND_PID_FILE):
+ #Parent
+ pass
+ else:
+ # Child
+ self.tracing(trace)
+ self.run()
return 0
def tracing(self, traceon):
"""Turn tracing on or off.
- traceon tracing flag
+ @param traceon: tracing flag
"""
if traceon == self.traceon:
return
self.traceon = traceon
if traceon:
- self.tracefile = open('/var/log/xend.trace', 'w+', 1)
+ self.tracefile = open(XEND_TRACE_FILE, 'w+', 1)
self.traceindent = 0
sys.settrace(self.trace)
try:
@@ -603,7 +610,6 @@ class Daemon:
xroot = XendRoot.instance()
log.info("Xend Daemon started")
self.createFactories()
- self.listenMgmt()
self.listenEvent()
self.listenNotifier()
self.listenVirq()
@@ -615,15 +621,9 @@ class Daemon:
self.domainCF = domain.DomainControllerFactory()
self.blkifCF = blkif.BlkifControllerFactory()
self.netifCF = netif.NetifControllerFactory()
+ self.usbifCF = usbif.UsbifControllerFactory()
self.consoleCF = console.ConsoleControllerFactory()
- def listenMgmt(self):
- protocol = MgmtProtocol(self)
- s = os.path.join(CONTROL_DIR, MGMT_SOCK)
- if os.path.exists(s):
- os.unlink(s)
- return reactor.listenUNIXDatagram(s, protocol)
-
def listenEvent(self):
protocol = EventFactory(self)
return reactor.listenTCP(EVENT_PORT, protocol)
@@ -639,110 +639,83 @@ class Daemon:
virqChan.registerClient(VirqClient(self))
def exit(self):
- reactor.diconnectAll()
+ reactor.disconnectAll()
sys.exit(0)
def getDomChannel(self, dom):
"""Get the channel to a domain.
- dom domain
-
- returns channel (or None)
+ @param dom: domain
+ @return: channel (or None)
"""
return self.channelF.getDomChannel(dom)
- def blkif_set_control_domain(self, dom, recreate=0):
- """Set the block device backend control domain.
- """
- return self.blkifCF.setControlDomain(dom, recreate=recreate)
-
- def blkif_get_control_domain(self, dom):
- """Get the block device backend control domain.
+ def createDomChannel(self, dom, local_port=0, remote_port=0):
+ """Get the channel to a domain, creating if necessary.
+
+ @param dom: domain
+ @param local_port: optional local port to re-use
+ @param remote_port: optional remote port to re-use
+ @return: channel
"""
- return self.blkifCF.getControlDomain()
-
+ return self.channelF.domChannel(dom, local_port=local_port,
+ remote_port=remote_port)
+
def blkif_create(self, dom, recreate=0):
- """Create a block device interface controller.
+ """Create or get a block device interface controller.
- Returns Deferred
+ Returns controller
"""
- d = self.blkifCF.createInstance(dom, recreate=recreate)
- return d
+ blkif = self.blkifCF.getController(dom)
+ blkif.daemon = self
+ return blkif
def blkifs(self):
- return [ x.sxpr() for x in self.blkifCF.getInstances() ]
+ return [ x.sxpr() for x in self.blkifCF.getControllers() ]
def blkif_get(self, dom):
- return self.blkifCF.getInstanceByDom(dom)
-
- def blkif_dev(self, dom, vdev):
- return self.blkifCF.getDomainDevice(dom, vdev)
+ return self.blkifCF.getControllerByDom(dom)
- def blkif_dev_create(self, dom, vdev, mode, segment, recreate=0):
- """Create a block device.
-
- Returns Deferred
- """
- ctrl = self.blkifCF.getInstanceByDom(dom)
- if not ctrl:
- raise XendError('No blkif controller: %d' % dom)
- d = ctrl.attachDevice(vdev, mode, segment, recreate=recreate)
- return d
-
- def netif_set_control_domain(self, dom, recreate=0):
- """Set the network interface backend control domain.
- """
- return self.netifCF.setControlDomain(dom, recreate=recreate)
-
- def netif_get_control_domain(self, dom):
- """Get the network interface backend control domain.
- """
- return self.netifCF.getControlDomain()
-
def netif_create(self, dom, recreate=0):
- """Create a network interface controller.
+ """Create or get a network interface controller.
"""
- return self.netifCF.createInstance(dom, recreate=recreate)
+ return self.netifCF.getController(dom)
def netifs(self):
- return [ x.sxpr() for x in self.netifCF.getInstances() ]
+ return [ x.sxpr() for x in self.netifCF.getControllers() ]
def netif_get(self, dom):
- return self.netifCF.getInstanceByDom(dom)
-
- def netif_dev_create(self, dom, vif, config, recreate=0):
- """Create a network device.
+ return self.netifCF.getControllerByDom(dom)
- """
- ctrl = self.netifCF.getInstanceByDom(dom)
- if not ctrl:
- raise XendError('No netif controller: %d' % dom)
- d = ctrl.attachDevice(vif, config, recreate=recreate)
- return d
+ def usbif_create(self, dom, recreate=0):
+ return self.usbifCF.getController(dom)
+
+ def usbifs(self):
+ return [ x.sxpr() for x in self.usbifCF.getControllers() ]
- def netif_dev(self, dom, vif):
- return self.netifCF.getDomainDevice(dom, vif)
+ def usbif_get(self, dom):
+ return self.usbifCF.getControllerByDom(dom)
def console_create(self, dom, console_port=None):
"""Create a console for a domain.
"""
- console = self.consoleCF.getInstanceByDom(dom)
+ console = self.consoleCF.getControllerByDom(dom)
if console is None:
- console = self.consoleCF.createInstance(dom, console_port)
+ console = self.consoleCF.createController(dom, console_port)
return console
def consoles(self):
- return [ c.sxpr() for c in self.consoleCF.getInstances() ]
+ return [ c.sxpr() for c in self.consoleCF.getControllers() ]
def get_consoles(self):
- return self.consoleCF.getInstances()
+ return self.consoleCF.getControllers()
def get_console(self, id):
- return self.consoleCF.getInstance(id)
+ return self.consoleCF.getControllerByIndex(id)
def get_domain_console(self, dom):
- return self.consoleCF.getInstanceByDom(dom)
+ return self.consoleCF.getControllerByDom(dom)
def console_disconnect(self, id):
"""Disconnect any connected console client.
@@ -752,13 +725,24 @@ class Daemon:
raise XendError('Invalid console id')
console.disconnect()
- def domain_shutdown(self, dom, reason):
+ def domain_shutdown(self, dom, reason, key=0):
"""Shutdown a domain.
"""
- ctrl = self.domainCF.getInstanceByDom(dom)
+ dom = int(dom)
+ ctrl = self.domainCF.getController(dom)
+ if not ctrl:
+ raise XendError('No domain controller: %s' % dom)
+ ctrl.shutdown(reason, key)
+ return 0
+
+ def domain_mem_target_set(self, dom, target):
+ """Set memory target for a domain.
+ """
+ dom = int(dom)
+ ctrl = self.domainCF.getController(dom)
if not ctrl:
- raise XendError('No domain controller: %d' % dom)
- ctrl.shutdown(reason)
+ raise XendError('No domain controller: %s' % dom)
+ ctrl.mem_target_set(target)
return 0
def instance():
diff --git a/tools/python/xen/xend/server/SrvDmesg.py b/tools/python/xen/xend/server/SrvDmesg.py
index 2e7b538c55..11fa4547c2 100644
--- a/tools/python/xen/xend/server/SrvDmesg.py
+++ b/tools/python/xen/xend/server/SrvDmesg.py
@@ -15,6 +15,9 @@ class SrvDmesg(SrvDir):
SrvDir.__init__(self)
self.xd = XendDmesg.instance()
+ def render_POST(self, req):
+ self.perform(req)
+
def render_GET(self, req):
try:
if self.use_sxp(req):
@@ -32,3 +35,7 @@ class SrvDmesg(SrvDir):
def info(self):
return self.xd.info()
+
+ def op_clear(self, op, req):
+ self.xd.clear()
+ return 0
diff --git a/tools/python/xen/xend/server/SrvDomain.py b/tools/python/xen/xend/server/SrvDomain.py
index 8975148819..d73a39bff5 100644
--- a/tools/python/xen/xend/server/SrvDomain.py
+++ b/tools/python/xen/xend/server/SrvDomain.py
@@ -21,10 +21,14 @@ class SrvDomain(SrvDir):
self.xconsole = XendConsole.instance()
def op_configure(self, op, req):
+ """Configure an existing domain.
+ Configure is unusual in that it requires a domain id,
+ not a domain name.
+ """
fn = FormFn(self.xd.domain_configure,
- [['dom', 'str'],
+ [['dom', 'int'],
['config', 'sxpr']])
- deferred = fn(req.args, {'dom': self.dom.name})
+ deferred = fn(req.args, {'dom': self.dom.dom})
deferred.addErrback(self._op_configure_err, req)
return deferred
@@ -43,8 +47,9 @@ class SrvDomain(SrvDir):
def op_shutdown(self, op, req):
fn = FormFn(self.xd.domain_shutdown,
[['dom', 'str'],
- ['reason', 'str']])
- val = fn(req.args, {'dom': self.dom.name})
+ ['reason', 'str'],
+ ['key', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
req.setResponseCode(http.ACCEPTED)
req.setHeader("Location", "%s/.." % req.prePathURL())
return val
@@ -53,7 +58,7 @@ class SrvDomain(SrvDir):
fn = FormFn(self.xd.domain_destroy,
[['dom', 'str'],
['reason', 'str']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
req.setHeader("Location", "%s/.." % req.prePathURL())
return val
@@ -61,7 +66,7 @@ class SrvDomain(SrvDir):
fn = FormFn(self.xd.domain_save,
[['dom', 'str'],
['file', 'str']])
- deferred = fn(req.args, {'dom': self.dom.name})
+ deferred = fn(req.args, {'dom': self.dom.id})
deferred.addCallback(self._op_save_cb, req)
deferred.addErrback(self._op_save_err, req)
return deferred
@@ -76,8 +81,10 @@ class SrvDomain(SrvDir):
def op_migrate(self, op, req):
fn = FormFn(self.xd.domain_migrate,
[['dom', 'str'],
- ['destination', 'str']])
- deferred = fn(req.args, {'dom': self.dom.name})
+ ['destination', 'str'],
+ ['live', 'int'],
+ ['resource', 'int']])
+ deferred = fn(req.args, {'dom': self.dom.id})
print 'op_migrate>', deferred
deferred.addCallback(self._op_migrate_cb, req)
deferred.addErrback(self._op_migrate_err, req)
@@ -103,7 +110,7 @@ class SrvDomain(SrvDir):
fn = FormFn(self.xd.domain_pincpu,
[['dom', 'str'],
['cpu', 'int']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
return val
def op_cpu_bvt_set(self, op, req):
@@ -114,19 +121,9 @@ class SrvDomain(SrvDir):
['warpvalue', 'int'],
['warpl', 'long'],
['warpu', 'long']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
return val
- def op_cpu_fbvt_set(self, op, req):
- fn = FormFn(self.xd.domain_cpu_fbvt_set,
- [['dom', 'str'],
- ['mcuadv', 'int'],
- ['warp', 'int'],
- ['warpl', 'int'],
- ['warpu', 'int']])
- val = fn(req.args, {'dom': self.dom.name})
- return val
-
def op_cpu_atropos_set(self, op, req):
fn = FormFn(self.xd.domain_cpu_atropos_set,
[['dom', 'str'],
@@ -134,21 +131,21 @@ class SrvDomain(SrvDir):
['slice', 'int'],
['latency', 'int'],
['xtratime', 'int']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
return val
def op_maxmem_set(self, op, req):
fn = FormFn(self.xd.domain_maxmem_set,
[['dom', 'str'],
['memory', 'int']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
return val
def op_device_create(self, op, req):
fn = FormFn(self.xd.domain_device_create,
[['dom', 'str'],
['config', 'sxpr']])
- d = fn(req.args, {'dom': self.dom.name})
+ d = fn(req.args, {'dom': self.dom.id})
return d
def op_device_destroy(self, op, req):
@@ -156,29 +153,44 @@ class SrvDomain(SrvDir):
[['dom', 'str'],
['type', 'str'],
['idx', 'str']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
return val
+ def op_device_configure(self, op, req):
+ fn = FormFn(self.xd.domain_device_configure,
+ [['dom', 'str'],
+ ['config', 'sxpr'],
+ ['idx', 'str']])
+ d = fn(req.args, {'dom': self.dom.id})
+ return d
+
def op_vifs(self, op, req):
- devs = self.xd.domain_vif_ls(self.dom.name)
+ devs = self.xd.domain_vif_ls(self.dom.id)
return [ dev.sxpr() for dev in devs ]
def op_vif(self, op, req):
fn = FormFn(self.xd.domain_vif_get,
[['dom', 'str'],
['vif', 'str']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
return val
def op_vbds(self, op, req):
- devs = self.xd.domain_vbd_ls(self.dom.name)
+ devs = self.xd.domain_vbd_ls(self.dom.id)
return [ dev.sxpr() for dev in devs ]
def op_vbd(self, op, req):
fn = FormFn(self.xd.domain_vbd_get,
[['dom', 'str'],
['vbd', 'str']])
- val = fn(req.args, {'dom': self.dom.name})
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_mem_target_set(self, op, req):
+ fn = FormFn(self.xd.domain_mem_target_set,
+ [['dom', 'str'],
+ ['target', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
return val
def render_POST(self, req):
@@ -186,7 +198,7 @@ class SrvDomain(SrvDir):
def render_GET(self, req):
op = req.args.get('op')
- if op and op[0] in ['vifs', 'vif', 'vbds', 'vbd']:
+ if op and op[0] in ['vifs', 'vif', 'vbds', 'vbd', 'mem_target_set']:
return self.perform(req)
if self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
@@ -240,4 +252,5 @@ class SrvDomain(SrvDir):
req.write('<form method="post" action="%s">' % url)
req.write('<br><input type="submit" name="op" value="migrate">')
req.write(' To host: <input type="text" name="destination">')
+ req.write('<input type="checkbox" name="live" value="1">Live')
req.write('</form>')
diff --git a/tools/python/xen/xend/server/SrvDomainDir.py b/tools/python/xen/xend/server/SrvDomainDir.py
index cc41c78c61..2fc8ee4877 100644
--- a/tools/python/xen/xend/server/SrvDomainDir.py
+++ b/tools/python/xen/xend/server/SrvDomainDir.py
@@ -5,11 +5,13 @@ from StringIO import StringIO
from twisted.protocols import http
from twisted.web import error
+from twisted.python.failure import Failure
from xen.xend import sxp
from xen.xend import XendDomain
from xen.xend.Args import FormFn
from xen.xend.XendError import XendError
+from xen.xend.XendLogging import log
from SrvDir import SrvDir
from SrvDomain import SrvDomain
@@ -45,7 +47,7 @@ class SrvDomainDir(SrvDir):
errmsg = ''
try:
configstring = req.args.get('config')[0]
- print 'config:', configstring
+ #print 'op_create>', 'config:', configstring
pin = sxp.Parser()
pin.input(configstring)
pin.input_eof()
@@ -58,18 +60,15 @@ class SrvDomainDir(SrvDir):
except sxp.ParseError, ex:
errmsg = 'Invalid configuration ' + str(ex)
if not ok:
- req.setResponseCode(http.BAD_REQUEST, errmsg)
- return errmsg
+ raise XendError(errmsg)
try:
deferred = self.xd.domain_create(config)
deferred.addCallback(self._op_create_cb, configstring, req)
- deferred.addErrback(self._op_create_err, req)
return deferred
except Exception, ex:
print 'op_create> Exception creating domain:'
traceback.print_exc()
- req.setResponseCode(http.BAD_REQUEST, "Error creating domain: " + str(ex))
- return str(ex)
+ raise XendError("Error creating domain: " + str(ex))
def _op_create_cb(self, dominfo, configstring, req):
"""Callback to handle deferred domain creation.
@@ -91,24 +90,15 @@ class SrvDomainDir(SrvDir):
out.close()
return val
- def _op_create_err(self, err, req):
- """Callback to handle errors in deferred domain creation.
- """
- print 'op_create> Deferred Exception creating domain:', err
- req.setResponseCode(http.BAD_REQUEST, "Error creating domain: " + str(err))
- return str(err)
-
def op_restore(self, op, req):
"""Restore a domain from file.
@return: deferred
"""
- #todo: return is deferred. May need ok and err callbacks.
fn = FormFn(self.xd.domain_restore,
[['file', 'str']])
deferred = fn(req.args)
deferred.addCallback(self._op_restore_cb, req)
- #deferred.addErrback(self._op_restore_err, req)
return deferred
def _op_restore_cb(self, dominfo, req):
@@ -126,11 +116,6 @@ class SrvDomainDir(SrvDir):
out.close()
return val
- def _op_restore_err(self, err, req):
- print 'op_create> Deferred Exception restoring domain:', err
- req.setResponseCode(http.BAD_REQUEST, "Error restoring domain: "+ str(err))
- return str(err)
-
def render_POST(self, req):
return self.perform(req)
diff --git a/tools/python/xen/xend/server/SrvNode.py b/tools/python/xen/xend/server/SrvNode.py
index b68b33ea84..c1b3ab560e 100644
--- a/tools/python/xen/xend/server/SrvNode.py
+++ b/tools/python/xen/xend/server/SrvNode.py
@@ -37,12 +37,6 @@ class SrvNode(SrvDir):
val = fn(req.args, {})
return val
- def op_cpu_fbvt_slice_set(self, op, req):
- fn = FormFn(self.xn.cpu_fbvt_slice_set,
- [['ctx_allow', 'int']])
- val = fn(req.args, {})
- return val
-
def render_POST(self, req):
return self.perform(req)
diff --git a/tools/python/xen/xend/server/SrvServer.py b/tools/python/xen/xend/server/SrvServer.py
index d1aed29c6b..353d6eed24 100644
--- a/tools/python/xen/xend/server/SrvServer.py
+++ b/tools/python/xen/xend/server/SrvServer.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Example xend HTTP and console server.
@@ -41,7 +41,7 @@ def create(port=None, interface=None, bridge=0):
port = xroot.get_xend_port()
if interface is None:
interface = xroot.get_xend_address()
- if bridge or xroot.rebooted:
+ if bridge:
Vifctl.network('start')
root = resource.Resource()
xend = SrvRoot()
diff --git a/tools/python/xen/xend/server/SrvUsbif.py b/tools/python/xen/xend/server/SrvUsbif.py
new file mode 100644
index 0000000000..bdd57524ce
--- /dev/null
+++ b/tools/python/xen/xend/server/SrvUsbif.py
@@ -0,0 +1,249 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+from twisted.protocols import http
+
+from xen.xend import sxp
+from xen.xend import XendDomain
+from xen.xend import XendConsole
+from xen.xend import PrettyPrint
+from xen.xend.Args import FormFn
+
+from SrvDir import SrvDir
+
+class SrvDomain(SrvDir):
+ """Service managing a single domain.
+ """
+
+ def __init__(self, dom):
+ SrvDir.__init__(self)
+ self.dom = dom
+ self.xd = XendDomain.instance()
+ self.xconsole = XendConsole.instance()
+
+ def op_configure(self, op, req):
+ """Configure an existing domain.
+ Configure is unusual in that it requires a domain id,
+ not a domain name.
+ """
+ fn = FormFn(self.xd.domain_configure,
+ [['dom', 'int'],
+ ['config', 'sxpr']])
+ deferred = fn(req.args, {'dom': self.dom.dom})
+ deferred.addErrback(self._op_configure_err, req)
+ return deferred
+
+ def _op_configure_err(self, err, req):
+ req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
+ return str(err)
+
+ def op_unpause(self, op, req):
+ val = self.xd.domain_unpause(self.dom.name)
+ return val
+
+ def op_pause(self, op, req):
+ val = self.xd.domain_pause(self.dom.name)
+ return val
+
+ def op_shutdown(self, op, req):
+ fn = FormFn(self.xd.domain_shutdown,
+ [['dom', 'str'],
+ ['reason', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ req.setResponseCode(http.ACCEPTED)
+ req.setHeader("Location", "%s/.." % req.prePathURL())
+ return val
+
+ def op_destroy(self, op, req):
+ fn = FormFn(self.xd.domain_destroy,
+ [['dom', 'str'],
+ ['reason', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ req.setHeader("Location", "%s/.." % req.prePathURL())
+ return val
+
+ def op_save(self, op, req):
+ fn = FormFn(self.xd.domain_save,
+ [['dom', 'str'],
+ ['file', 'str']])
+ deferred = fn(req.args, {'dom': self.dom.id})
+ deferred.addCallback(self._op_save_cb, req)
+ deferred.addErrback(self._op_save_err, req)
+ return deferred
+
+ def _op_save_cb(self, val, req):
+ return 0
+
+ def _op_save_err(self, err, req):
+ req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
+ return str(err)
+
+ def op_migrate(self, op, req):
+ fn = FormFn(self.xd.domain_migrate,
+ [['dom', 'str'],
+ ['destination', 'str'],
+ ['live', 'int']])
+ deferred = fn(req.args, {'dom': self.dom.id})
+ print 'op_migrate>', deferred
+ deferred.addCallback(self._op_migrate_cb, req)
+ deferred.addErrback(self._op_migrate_err, req)
+ return deferred
+
+ def _op_migrate_cb(self, info, req):
+ print '_op_migrate_cb>', info, req
+ #req.setResponseCode(http.ACCEPTED)
+ host = info.dst_host
+ port = info.dst_port
+ dom = info.dst_dom
+ url = "http://%s:%d/xend/domain/%d" % (host, port, dom)
+ req.setHeader("Location", url)
+ print '_op_migrate_cb> url=', url
+ return url
+
+ def _op_migrate_err(self, err, req):
+ print '_op_migrate_err>', err, req
+ req.setResponseCode(http.BAD_REQUEST, "Error: "+ str(err))
+ return str(err)
+
+ def op_pincpu(self, op, req):
+ fn = FormFn(self.xd.domain_pincpu,
+ [['dom', 'str'],
+ ['cpu', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_cpu_bvt_set(self, op, req):
+ fn = FormFn(self.xd.domain_cpu_bvt_set,
+ [['dom', 'str'],
+ ['mcuadv', 'int'],
+ ['warpback', 'int'],
+ ['warpvalue', 'int'],
+ ['warpl', 'long'],
+ ['warpu', 'long']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_cpu_fbvt_set(self, op, req):
+ fn = FormFn(self.xd.domain_cpu_fbvt_set,
+ [['dom', 'str'],
+ ['mcuadv', 'int'],
+ ['warp', 'int'],
+ ['warpl', 'int'],
+ ['warpu', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_cpu_atropos_set(self, op, req):
+ fn = FormFn(self.xd.domain_cpu_atropos_set,
+ [['dom', 'str'],
+ ['period', 'int'],
+ ['slice', 'int'],
+ ['latency', 'int'],
+ ['xtratime', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_maxmem_set(self, op, req):
+ fn = FormFn(self.xd.domain_maxmem_set,
+ [['dom', 'str'],
+ ['memory', 'int']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_device_create(self, op, req):
+ fn = FormFn(self.xd.domain_device_create,
+ [['dom', 'str'],
+ ['config', 'sxpr']])
+ d = fn(req.args, {'dom': self.dom.id})
+ return d
+
+ def op_device_destroy(self, op, req):
+ fn = FormFn(self.xd.domain_device_destroy,
+ [['dom', 'str'],
+ ['type', 'str'],
+ ['idx', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vifs(self, op, req):
+ devs = self.xd.domain_vif_ls(self.dom.id)
+ return [ dev.sxpr() for dev in devs ]
+
+ def op_vif(self, op, req):
+ fn = FormFn(self.xd.domain_vif_get,
+ [['dom', 'str'],
+ ['vif', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def op_vbds(self, op, req):
+ devs = self.xd.domain_vbd_ls(self.dom.id)
+ return [ dev.sxpr() for dev in devs ]
+
+ def op_vbd(self, op, req):
+ fn = FormFn(self.xd.domain_vbd_get,
+ [['dom', 'str'],
+ ['vbd', 'str']])
+ val = fn(req.args, {'dom': self.dom.id})
+ return val
+
+ def render_POST(self, req):
+ return self.perform(req)
+
+ def render_GET(self, req):
+ op = req.args.get('op')
+ if op and op[0] in ['vifs', 'vif', 'vbds', 'vbd']:
+ return self.perform(req)
+ if self.use_sxp(req):
+ req.setHeader("Content-Type", sxp.mime_type)
+ sxp.show(self.dom.sxpr(), out=req)
+ else:
+ req.write('<html><head></head><body>')
+ self.print_path(req)
+ #self.ls()
+ req.write('<p>%s</p>' % self.dom)
+ if self.dom.console:
+ cinfo = self.dom.console
+ cid = str(cinfo.console_port)
+ #todo: Local xref: need to know server prefix.
+ req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
+ % (cid, cid))
+ req.write('<p><a href="%s">Connect to console</a></p>'
+ % cinfo.uri())
+ if self.dom.config:
+ req.write("<code><pre>")
+ PrettyPrint.prettyprint(self.dom.config, out=req)
+ req.write("</pre></code>")
+ self.form(req)
+ req.write('</body></html>')
+ return ''
+
+ def form(self, req):
+ url = req.prePathURL()
+ req.write('<form method="post" action="%s">' % url)
+ req.write('<input type="submit" name="op" value="unpause">')
+ req.write('<input type="submit" name="op" value="pause">')
+ req.write('</form>')
+
+ req.write('<form method="post" action="%s">' % url)
+ req.write('<input type="submit" name="op" value="destroy">')
+ req.write('<input type="radio" name="reason" value="halt" checked>Halt')
+ req.write('<input type="radio" name="reason" value="reboot">Reboot')
+ req.write('</form>')
+
+ req.write('<form method="post" action="%s">' % url)
+ req.write('<input type="submit" name="op" value="shutdown">')
+ req.write('<input type="radio" name="reason" value="poweroff" checked>Poweroff')
+ req.write('<input type="radio" name="reason" value="halt">Halt')
+ req.write('<input type="radio" name="reason" value="reboot">Reboot')
+ req.write('</form>')
+
+ req.write('<form method="post" action="%s">' % url)
+ req.write('<br><input type="submit" name="op" value="save">')
+ req.write(' To file: <input type="text" name="file">')
+ req.write('</form>')
+
+ req.write('<form method="post" action="%s">' % url)
+ req.write('<br><input type="submit" name="op" value="migrate">')
+ req.write(' To host: <input type="text" name="destination">')
+ req.write('<input type="checkbox" name="live" value="1">Live')
+ req.write('</form>')
diff --git a/tools/python/xen/xend/server/blkif.py b/tools/python/xen/xend/server/blkif.py
index a51754bad3..f40f7d5b6b 100755
--- a/tools/python/xen/xend/server/blkif.py
+++ b/tools/python/xen/xend/server/blkif.py
@@ -1,56 +1,287 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Support for virtual block devices.
+"""
from twisted.internet import defer
-#defer.Deferred.debug = 1
from xen.xend import sxp
+from xen.xend import Blkctl
from xen.xend.XendLogging import log
+from xen.xend.XendError import XendError, VmError
+import os
+import re
+import string
import channel
import controller
from messages import *
-class BlkifControllerFactory(controller.ControllerFactory):
- """Factory for creating block device interface controllers.
- Also handles the 'back-end' channel to the device driver domain.
+from xen.util.ip import _readline, _readlines
+
+def expand_dev_name(name):
+ if re.match( '^/dev/', name ):
+ return name
+ else:
+ return '/dev/' + name
+
+def check_mounted(self, name):
+ mode = None
+ name = expand_dev_name(name)
+ lines = _readlines(os.popen('mount 2>/dev/null'))
+ exp = re.compile('^' + name + ' .*[\(,]r(?P<mode>[ow])[,\)]')
+ for line in lines:
+ pm = exp.match(line)
+ if not pm: continue
+ mode = pm.group('mode')
+ break
+ if mode is 'w':
+ return mode
+ if mode is 'o':
+ mode = 'r'
+ blkifs = self.ctrl.daemon.blkifs()
+ for blkif in blkifs:
+ if blkif[1][1] is self.ctrl.dom:
+ continue
+ for dev in self.ctrl.daemon.blkif_get(blkif[1][1]).getDevices():
+ if dev.type == 'phy' and name == expand_dev_name(dev.params):
+ mode = dev.mode
+ if 'w' in mode:
+ return 'w'
+ if mode and 'r' in mode:
+ return 'r'
+ return None
+
+def blkdev_name_to_number(name):
+ """Take the given textual block-device name (e.g., '/dev/sda1',
+ 'hda') and return the device number used by the OS. """
+
+ n = expand_dev_name(name)
+
+ try:
+ return os.stat(n).st_rdev
+ except Exception, ex:
+ log.debug("exception looking up device number for %s: %s", name, ex)
+ pass
+
+ if re.match( '/dev/sd[a-p]([0-9]|1[0-5])', n):
+ return 8 * 256 + 16 * (ord(n[7:8]) - ord('a')) + int(n[8:])
+
+ if re.match( '/dev/hd[a-t]([1-9]|[1-5][0-9]|6[0-3])?', n):
+ ide_majors = [ 3, 22, 33, 34, 56, 57, 88, 89, 90, 91 ]
+ major = ide_majors[(ord(n[7:8]) - ord('a')) / 2]
+ minor = ((ord(n[7:8]) - ord('a')) % 2) * 64 + int(n[8:] or 0)
+ return major * 256 + minor
+
+ # see if this is a hex device number
+ if re.match( '^(0x)?[0-9a-fA-F]+$', name ):
+ return string.atoi(name,16)
+
+ return None
+
+def blkdev_segment(name):
+ """Take the given block-device name (e.g. '/dev/sda1', 'hda')
+ and return a dictionary { device, start_sector,
+ nr_sectors, type }
+ device: Device number of the given partition
+ start_sector: Index of first sector of the partition
+ nr_sectors: Number of sectors comprising this partition
+ type: 'Disk' or identifying name for partition type
+ """
+ val = None
+ n = blkdev_name_to_number(name)
+ if n:
+ val = { 'device' : n,
+ 'start_sector' : long(0),
+ 'nr_sectors' : long(1L<<63),
+ 'type' : 'Disk' }
+ return val
+
+class BlkifBackendController(controller.BackendController):
+ """ Handler for the 'back-end' channel to a block device driver domain.
"""
- def __init__(self):
- controller.ControllerFactory.__init__(self)
+ def __init__(self, factory, dom):
+ controller.BackendController.__init__(self, factory, dom)
+ self.addMethod(CMSG_BLKIF_BE,
+ CMSG_BLKIF_BE_DRIVER_STATUS,
+ self.recv_be_driver_status)
+ self.registerChannel()
- self.majorTypes = [ CMSG_BLKIF_BE ]
+ def recv_be_driver_status(self, msg, req):
+ """Request handler for be_driver_status messages.
+
+ @param msg: message
+ @type msg: xu message
+ @param req: request flag (true if the msg is a request)
+ @type req: bool
+ """
+ val = unpackMsg('blkif_be_driver_status_t', msg)
+ status = val['status']
- self.subTypes = {
- CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
- }
- self.attached = 1
- self.registerChannel()
+class BlkifBackendInterface(controller.BackendInterface):
+ """ Handler for the 'back-end' channel to a block device driver domain
+ on behalf of a front-end domain.
+ Must be connected using connect() before it can be used.
+ Do not create directly - use getBackendInterface() on the BlkifController.
+ """
- def createInstance(self, dom, recreate=0):
+ def __init__(self, ctrl, dom, handle):
+ controller.BackendInterface.__init__(self, ctrl, dom, handle)
+ self.connected = 0
+ self.evtchn = None
+ self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
+
+ def __str__(self):
+ return '<BlkifBackendInterface %d %d>' % (self.controller.dom, self.dom)
+
+ def getEventChannelBackend(self):
+ val = 0
+ if self.evtchn:
+ val = self.evtchn['port1']
+ return val
+
+ def getEventChannelFrontend(self):
+ val = 0
+ if self.evtchn:
+ val = self.evtchn['port2']
+ return val
+
+ def connect(self, recreate=0):
+ """Connect to the blkif control interface.
+
+ @param recreate: true if after xend restart
+ @return: deferred
+ """
+ log.debug("Connecting blkif %s", str(self))
+ if recreate or self.connected:
+ d = defer.succeed(self)
+ else:
+ d = self.send_be_create()
+ d.addCallback(self.respond_be_create)
+ return d
+
+ def send_be_create(self):
+ d = defer.Deferred()
+ msg = packMsg('blkif_be_create_t',
+ { 'domid' : self.controller.dom,
+ 'blkif_handle' : self.handle })
+ self.writeRequest(msg, response=d)
+ return d
+
+ def respond_be_create(self, msg):
+ val = unpackMsg('blkif_be_create_t', msg)
+ self.connected = 1
+ return self
+
+ def destroy(self):
+ """Disconnect from the blkif control interface and destroy it.
+ """
+ def cb_destroy(val):
+ self.send_be_destroy()
+ self.close()
+ d = defer.Deferred()
+ d.addCallback(cb_destroy)
+ self.send_be_disconnect(response=d)
+
+ def send_be_disconnect(self, response=None):
+ msg = packMsg('blkif_be_disconnect_t',
+ { 'domid' : self.controller.dom,
+ 'blkif_handle' : self.handle })
+ self.writeRequest(msg, response=response)
+
+ def send_be_destroy(self, response=None):
+ msg = packMsg('blkif_be_destroy_t',
+ { 'domid' : self.controller.dom,
+ 'blkif_handle' : self.handle })
+ self.writeRequest(msg, response=response)
+
+ def connectInterface(self, val):
+ self.evtchn = channel.eventChannel(self.dom, self.controller.dom)
+ log.debug("Connecting blkif to event channel %s ports=%d:%d",
+ str(self), self.evtchn['port1'], self.evtchn['port2'])
+ msg = packMsg('blkif_be_connect_t',
+ { 'domid' : self.controller.dom,
+ 'blkif_handle' : self.handle,
+ 'evtchn' : self.getEventChannelBackend(),
+ 'shmem_frame' : val['shmem_frame'] })
+ d = defer.Deferred()
+ d.addCallback(self.respond_be_connect)
+ self.writeRequest(msg, response=d)
+
+ def respond_be_connect(self, msg):
+ """Response handler for a be_connect message.
+
+ @param msg: message
+ @type msg: xu message
+ """
+ val = unpackMsg('blkif_be_connect_t', msg)
+ self.status = BLKIF_INTERFACE_STATUS_CONNECTED
+ self.send_fe_interface_status()
+
+ def send_fe_interface_status(self, response=None):
+ msg = packMsg('blkif_fe_interface_status_t',
+ { 'handle' : self.handle,
+ 'status' : self.status,
+ 'domid' : self.dom,
+ 'evtchn' : self.getEventChannelFrontend() })
+ self.controller.writeRequest(msg, response=response)
+
+ def interfaceDisconnected(self):
+ self.status = BLKIF_INTERFACE_STATUS_DISCONNECTED
+ #todo?: Do this: self.evtchn = None
+ self.send_fe_interface_status()
+
+ def interfaceChanged(self):
+ """Notify the front-end that devices have been added or removed.
+ The front-end should then probe for devices.
+ """
+ msg = packMsg('blkif_fe_interface_status_t',
+ { 'handle' : self.handle,
+ 'status' : BLKIF_INTERFACE_STATUS_CHANGED,
+ 'domid' : self.dom,
+ 'evtchn' : 0 })
+ self.controller.writeRequest(msg)
+
+class BlkifControllerFactory(controller.SplitControllerFactory):
+ """Factory for creating block device interface controllers.
+ """
+
+ def __init__(self):
+ controller.SplitControllerFactory.__init__(self)
+
+ def createController(self, dom, recreate=0):
"""Create a block device controller for a domain.
@param dom: domain
@type dom: int
@param recreate: if true it's a recreate (after xend restart)
@type recreate: bool
- @return: deferred
- @rtype: twisted.internet.defer.Deferred
+ @return: block device controller
+ @rtype: BlkifController
"""
- d = defer.Deferred()
- blkif = self.getInstanceByDom(dom)
- if blkif:
- d.callback(blkif)
- else:
+ blkif = self.getControllerByDom(dom)
+ if blkif is None:
blkif = BlkifController(self, dom)
- self.addInstance(blkif)
- if recreate:
- d.callback(blkif)
- else:
- d1 = defer.Deferred()
- d1.addCallback(self.respond_be_create, d)
- d1.addErrback(d.errback)
- blkif.send_be_create(response=d1)
- return d
+ self.addController(blkif)
+ return blkif
+
+ def createBackendController(self, dom):
+ """Create a block device backend controller.
+
+ @param dom: backend domain
+ @return: backend controller
+ """
+ return BlkifBackendController(self, dom)
+
+ def createBackendInterface(self, ctrl, dom, handle):
+ """Create a block device backend interface.
+
+ @param ctrl: controller
+ @param dom: backend domain
+ @param handle: interface handle
+ @return: backend interface
+ """
+ return BlkifBackendInterface(ctrl, dom, handle)
def getDomainDevices(self, dom):
"""Get the block devices for a domain.
@@ -60,398 +291,305 @@ class BlkifControllerFactory(controller.ControllerFactory):
@return: devices
@rtype: [device]
"""
- blkif = self.getInstanceByDom(dom)
+ blkif = self.getControllerByDom(dom)
return (blkif and blkif.getDevices()) or []
- def getDomainDevice(self, dom, vdev):
+ def getDomainDevice(self, dom, idx):
"""Get a block device from a domain.
@param dom: domain
@type dom: int
- @param vdev: device index
- @type vedv: int
+ @param idx: device index
+ @type idx: int
@return: device
@rtype: device
"""
- blkif = self.getInstanceByDom(dom)
- return (blkif and blkif.getDevice(vdev)) or None
+ blkif = self.getControllerByDom(dom)
+ return (blkif and blkif.getDevice(idx)) or None
- def setControlDomain(self, dom, recreate=0):
- """Set the back-end block device controller domain.
+class BlkDev(controller.SplitDev):
+ """Info record for a block device.
+ """
- @param dom: domain
- @type dom: int
- @param recreate: if true it's a recreate (after xend restart)
- @type recreate: int
- """
- if self.dom == dom: return
- self.deregisterChannel()
- if not recreate:
- self.attached = 0
- self.dom = dom
- self.registerChannel()
+ def __init__(self, idx, ctrl, config):
+ controller.SplitDev.__init__(self, idx, ctrl)
+ self.dev = None
+ self.uname = None
+ self.vdev = None
+ self.mode = None
+ self.type = None
+ self.params = None
+ self.node = None
+ self.device = None
+ self.start_sector = None
+ self.nr_sectors = None
+ self.ctrl = ctrl
+ self.configure(config)
+
+ def configure(self, config):
+ self.config = config
+ self.uname = sxp.child_value(config, 'uname')
+ if not self.uname:
+ raise VmError('vbd: Missing uname')
+ # Split into type and type-specific params (which are passed to the
+ # type-specific control script).
+ (self.type, self.params) = string.split(self.uname, ':', 1)
+ self.dev = sxp.child_value(config, 'dev')
+ if not self.dev:
+ raise VmError('vbd: Missing dev')
+ self.mode = sxp.child_value(config, 'mode', 'r')
+ # todo: The 'dev' should be looked up in the context of the domain.
+ self.vdev = blkdev_name_to_number(self.dev)
+ if not self.vdev:
+ raise VmError('vbd: Device not found: %s' % self.dev)
+ try:
+ self.backendDomain = int(sxp.child_value(config, 'backend', '0'))
+ except:
+ raise XendError('invalid backend domain')
+
+ def recreate(self, savedinfo):
+ node = sxp.child_value(savedinfo, 'node')
+ self.setNode(node)
+
+ def attach(self):
+ node = Blkctl.block('bind', self.type, self.params)
+ self.setNode(node)
+ return self.attachBackend()
+
+ def unbind(self):
+ if self.node is None: return
+ log.debug("Unbinding vbd (type %s) from %s"
+ % (self.type, self.node))
+ Blkctl.block('unbind', self.type, self.node)
+
+ def setNode(self, node):
+
+ # NOTE:
+ # This clause is testing code for storage system experiments.
+ # Add a new disk type that will just pass an opaque id in the
+ # start_sector and use an experimental device type.
+ # Please contact andrew.warfield@cl.cam.ac.uk with any concerns.
+ if self.type == 'amorfs':
+ self.node = node
+ self.device = 61440 # (240,0)
+ self.start_sector = long(self.params)
+ self.nr_sectors = long(0)
+ return
+ # done.
+
+ mounted_mode = check_mounted(self, node)
+ if not '!' in self.mode and mounted_mode:
+ if mounted_mode is "w":
+ raise VmError("vbd: Segment %s is in writable use" %
+ self.uname)
+ elif 'w' in self.mode:
+ raise VmError("vbd: Segment %s is in read-only use" %
+ self.uname)
+ segment = blkdev_segment(node)
+ if not segment:
+ raise VmError("vbd: Segment not found: uname=%s" % self.uname)
+ self.node = node
+ self.device = segment['device']
+ self.start_sector = segment['start_sector']
+ self.nr_sectors = segment['nr_sectors']
- def getControlDomain(self):
- """Get the back-end block device controller domain.
+ def readonly(self):
+ return 'w' not in self.mode
- @return: domain
- @rtype: int
- """
- return self.dom
+ def sxpr(self):
+ val = ['vbd',
+ ['idx', self.idx],
+ ['vdev', self.vdev],
+ ['device', self.device],
+ ['mode', self.mode]]
+ if self.dev:
+ val.append(['dev', self.dev])
+ if self.uname:
+ val.append(['uname', self.uname])
+ if self.node:
+ val.append(['node', self.node])
+ if self.index is not None:
+ val.append(['index', self.index])
+ return val
- def reattachDevice(self, dom, vdev):
- """Reattach a device (on changing control domain).
+ def destroy(self, change=0):
+ """Destroy the device. If 'change' is true notify the front-end interface.
- @param dom: domain
- @type dom: int
- @param vdev: device index
- @type vdev: int
+ @param change: change flag
"""
- blkif = self.getInstanceByDom(dom)
- if blkif:
- blkif.reattachDevice(vdev)
- self.attached = self.devicesAttached()
- if self.attached:
- self.reattached()
-
- def devicesAttached(self):
- """Check if all devices are attached.
-
- @return: true if all devices attached
- @rtype: bool
+ log.debug("Destroying vbd domain=%d idx=%s", self.controller.dom, self.idx)
+ d = self.send_be_vbd_destroy()
+ if change:
+ d.addCallback(lambda val: self.interfaceChanged())
+ d.addCallback(lambda val: self.unbind())
+
+ def interfaceChanged(self):
+ """Tell the back-end to notify the front-end that a device has been
+ added or removed.
"""
- attached = 1
- for blkif in self.getInstances():
- if not blkif.attached:
- attached = 0
- break
- return attached
-
- def reattached(self):
- """Notify all block interfaces we have been reattached
- (after changing control domain).
- """
- for blkif in self.getInstances():
- blkif.reattached()
-
- def respond_be_create(self, msg, d):
- """Response handler for a be_create message.
- Calls I{d} with the block interface created.
+ self.getBackendInterface().interfaceChanged()
- @param msg: message
- @type msg: xu message
- @param d: deferred to call
- @type d: Deferred
- """
- val = unpackMsg('blkif_be_create_t', msg)
- blkif = self.getInstanceByDom(val['domid'])
- d.callback(blkif)
-
- def respond_be_connect(self, msg):
- """Response handler for a be_connect message.
+ def attachBackend(self):
+ """Attach the device to its controller.
- @param msg: message
- @type msg: xu message
"""
- val = unpackMsg('blkif_be_connect_t', msg)
- blkif = self.getInstanceByDom(val['domid'])
- if blkif:
- blkif.send_fe_interface_status_changed()
- else:
- pass
-
- def respond_be_vbd_create(self, msg, dev, d):
+ backend = self.getBackendInterface()
+ d1 = backend.connect()
+ d2 = defer.Deferred()
+ d2.addCallback(self.send_be_vbd_create)
+ d1.chainDeferred(d2)
+ return d2
+
+ def send_be_vbd_create(self, val):
+ d = defer.Deferred()
+ d.addCallback(self.respond_be_vbd_create)
+ backend = self.getBackendInterface()
+ msg = packMsg('blkif_be_vbd_create_t',
+ { 'domid' : self.controller.dom,
+ 'blkif_handle' : backend.handle,
+ 'vdevice' : self.vdev,
+ 'readonly' : self.readonly() })
+ backend.writeRequest(msg, response=d)
+ return d
+
+ def respond_be_vbd_create(self, msg):
"""Response handler for a be_vbd_create message.
- Tries to grow the vbd, and passes the deferred I{d} on for
- the grow to call.
+ Tries to grow the vbd.
@param msg: message
@type msg: xu message
- @param dev: device
- @type dev: BlkDev
- @param d: deferred to call
- @type d: Deferred
"""
val = unpackMsg('blkif_be_vbd_create_t', msg)
- blkif = self.getInstanceByDom(val['domid'])
- if blkif:
- d1 = defer.Deferred()
- d1.addCallback(self.respond_be_vbd_grow, dev, d)
- if d: d1.addErrback(d.errback)
- blkif.send_be_vbd_grow(val['vdevice'], response=d1)
- else:
- pass
+ d = self.send_be_vbd_grow()
+ d.addCallback(self.respond_be_vbd_grow)
+ return d
- def respond_be_vbd_grow(self, msg, dev, d):
+ def send_be_vbd_grow(self):
+ d = defer.Deferred()
+ backend = self.getBackendInterface()
+ msg = packMsg('blkif_be_vbd_grow_t',
+ { 'domid' : self.controller.dom,
+ 'blkif_handle' : backend.handle,
+ 'vdevice' : self.vdev,
+ 'extent.device' : self.device,
+ 'extent.sector_start' : self.start_sector,
+ 'extent.sector_length' : self.nr_sectors })
+ backend.writeRequest(msg, response=d)
+ return d
+
+ def respond_be_vbd_grow(self, msg):
"""Response handler for a be_vbd_grow message.
@param msg: message
@type msg: xu message
- @param dev: device
- @type dev: BlkDev
- @param d: deferred to call
- @type d: Deferred or None
"""
val = unpackMsg('blkif_be_vbd_grow_t', msg)
- # Check status?
status = val['status']
if status != BLKIF_BE_STATUS_OKAY:
- log.debug("Error: Adding extent to vbd failed! (device %x)",
- val['extent.device'])
- # what to do here to abort????
+ raise XendError("Adding extent to vbd failed: device %s, error %d"
+ % (sxp.to_string(self.config), status))
+ return self
- if self.attached:
- if d:
- d.callback(dev)
- else:
- self.reattachDevice(val['domid'], val['vdevice'])
-
- def recv_be_driver_status_changed(self, msg, req):
- """Request handler for be_driver_status_changed messages.
+ def send_be_vbd_destroy(self):
+ d = defer.Deferred()
+ backend = self.getBackendInterface()
+ msg = packMsg('blkif_be_vbd_destroy_t',
+ { 'domid' : self.controller.dom,
+ 'blkif_handle' : backend.handle,
+ 'vdevice' : self.vdev })
+ self.controller.delDevice(self.vdev)
+ backend.writeRequest(msg, response=d)
+ return d
- @param msg: message
- @type msg: xu message
- @param req: request flag (true if the msg is a request)
- @type req: bool
- """
- val = unpackMsg('blkif_be_driver_status_changed_t', msg)
- status = val['status']
- if status == BLKIF_DRIVER_STATUS_UP and not self.attached:
- for blkif in self.getInstances():
- blkif.detach()
-
-class BlkDev(controller.Dev):
- """Info record for a block device.
- """
-
- def __init__(self, ctrl, vdev, mode, segment):
- controller.Dev.__init__(self, segment['device'], ctrl)
- self.dev = None
- self.uname = None
- self.vdev = vdev
- self.mode = mode
- self.device = segment['device']
- self.start_sector = segment['start_sector']
- self.nr_sectors = segment['nr_sectors']
- self.attached = 1
-
- def readonly(self):
- return 'w' not in self.mode
- def sxpr(self):
- val = ['blkdev',
- ['idx', self.idx],
- ['vdev', self.vdev],
- ['device', self.device],
- ['mode', self.mode]]
- if self.dev:
- val.append(['dev', self.dev])
- if self.uname:
- val.append(['uname', self.uname])
- return val
-
- def destroy(self):
- log.debug("Destroying vbd domain=%d vdev=%d", self.controller.dom, self.vdev)
- self.controller.send_be_vbd_destroy(self.vdev)
-
-class BlkifController(controller.Controller):
+class BlkifController(controller.SplitController):
"""Block device interface controller. Handles all block devices
for a domain.
"""
def __init__(self, factory, dom):
- controller.Controller.__init__(self, factory, dom)
- self.devices = {}
-
- self.majorTypes = [ CMSG_BLKIF_FE ]
-
- self.subTypes = {
- CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED:
- self.recv_fe_driver_status_changed,
- CMSG_BLKIF_FE_INTERFACE_CONNECT :
- self.recv_fe_interface_connect,
- }
- self.attached = 1
- self.evtchn = None
+ """Create a block device controller.
+ Do not call directly - use createController() on the factory instead.
+ """
+ controller.SplitController.__init__(self, factory, dom)
+ self.addMethod(CMSG_BLKIF_FE,
+ CMSG_BLKIF_FE_DRIVER_STATUS,
+ self.recv_fe_driver_status)
+ self.addMethod(CMSG_BLKIF_FE,
+ CMSG_BLKIF_FE_INTERFACE_CONNECT,
+ self.recv_fe_interface_connect)
self.registerChannel()
def sxpr(self):
val = ['blkif', ['dom', self.dom]]
- if self.evtchn:
- val.append(['evtchn',
- self.evtchn['port1'],
- self.evtchn['port2']])
return val
- def getDevices(self):
- return self.devices.values()
-
- def getDevice(self, vdev):
- return self.devices.get(vdev)
-
- def addDevice(self, vdev, mode, segment):
+ def addDevice(self, idx, config):
"""Add a device to the device table.
@param vdev: device index
@type vdev: int
- @param mode: read/write mode
- @type mode: string
- @param segment: segment
- @type segment: int
+ @param config: device configuration
@return: device
@rtype: BlkDev
"""
- if vdev in self.devices: return None
- dev = BlkDev(self, vdev, mode, segment)
- self.devices[vdev] = dev
+ if idx in self.devices:
+ raise XendError('device exists: ' + str(idx))
+ dev = BlkDev(idx, self, config )
+ self.devices[idx] = dev
return dev
- def attachDevice(self, vdev, mode, segment, recreate=0):
+ def attachDevice(self, idx, config, recreate=0):
"""Attach a device to the specified interface.
On success the returned deferred will be called with the device.
- @param vdev: device index
- @type vdev: int
- @param mode: read/write mode
- @type mode: string
- @param segment: segment
- @type segment: int
+ @param idx: device id
+ @param config: device configuration
@param recreate: if true it's being recreated (after xend restart)
@type recreate: bool
@return: deferred
@rtype: Deferred
"""
- dev = self.addDevice(vdev, mode, segment)
- if not dev: return -1
- d = defer.Deferred()
+ dev = self.addDevice(idx, config)
if recreate:
- d.callback(dev)
+ dev.recreate(recreate)
+ d = defer.succeed(dev)
else:
- d1 = defer.Deferred()
- d1.addCallback(self.factory.respond_be_vbd_create, dev, d)
- d1.addErrback(d.errback)
- self.send_be_vbd_create(vdev, response=d1)
+ d = dev.attach()
return d
def destroy(self):
- def cb_destroy(val):
- self.send_be_destroy()
+ """Destroy the controller and all devices.
+ """
log.debug("Destroying blkif domain=%d", self.dom)
- d = defer.Deferred()
- d.addCallback(cb_destroy)
- self.send_be_disconnect(response=d)
+ self.destroyDevices()
+ self.destroyBackends()
def destroyDevices(self):
+ """Destroy all devices.
+ """
for dev in self.getDevices():
dev.destroy()
- def detach(self):
- """Detach all devices, when the back-end control domain has changed.
- """
- self.attached = 0
- for dev in self.devices.values():
- dev.attached = 0
- d1 = defer.Deferred()
- d1.addCallback(self.factory.respond_be_vbd_create, None, None)
- self.send_be_vbd_create(vdev, response=d1)
-
- def reattachDevice(self, vdev):
- """Reattach a device, when the back-end control domain has changed.
- """
- dev = self.devices[vdev]
- dev.attached = 1
- attached = 1
- for dev in self.devices.values():
- if not dev.attached:
- attached = 0
- break
- self.attached = attached
- return self.attached
-
- def reattached(self):
- """All devices have been reattached after the back-end control
- domain has changed.
- """
- msg = packMsg('blkif_fe_interface_status_changed_t',
- { 'handle' : 0,
- 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED})
- self.writeRequest(msg)
-
- def recv_fe_driver_status_changed(self, msg, req):
- msg = packMsg('blkif_fe_interface_status_changed_t',
- { 'handle' : 0,
- 'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED,
- 'evtchn' : 0 })
- self.writeRequest(msg)
-
- def recv_fe_interface_connect(self, msg, req):
- val = unpackMsg('blkif_fe_interface_connect_t', msg)
- self.evtchn = channel.eventChannel(0, self.dom)
- log.debug("Connecting blkif to event channel dom=%d ports=%d:%d",
- self.dom, self.evtchn['port1'], self.evtchn['port2'])
- msg = packMsg('blkif_be_connect_t',
- { 'domid' : self.dom,
- 'blkif_handle' : val['handle'],
- 'evtchn' : self.evtchn['port1'],
- 'shmem_frame' : val['shmem_frame'] })
- d = defer.Deferred()
- d.addCallback(self.factory.respond_be_connect)
- self.factory.writeRequest(msg, response=d)
-
- def send_fe_interface_status_changed(self, response=None):
- msg = packMsg('blkif_fe_interface_status_changed_t',
- { 'handle' : 0,
- 'status' : BLKIF_INTERFACE_STATUS_CONNECTED,
- 'evtchn' : self.evtchn['port2'] })
- self.writeRequest(msg, response=response)
+ def destroyBackends(self):
+ for backend in self.getBackendInterfaces():
+ backend.destroy()
- def send_be_create(self, response=None):
- msg = packMsg('blkif_be_create_t',
- { 'domid' : self.dom,
- 'blkif_handle' : 0 })
- self.factory.writeRequest(msg, response=response)
+ def recv_fe_driver_status(self, msg, req):
+ val = unpackMsg('blkif_fe_driver_status_t', msg)
+ print 'recv_fe_driver_status>', val
+ for backend in self.getBackendInterfaces():
+ backend.interfaceDisconnected()
- def send_be_disconnect(self, response=None):
- log.debug('>BlkifController>send_be_disconnect> dom=%d', self.dom)
- msg = packMsg('blkif_be_disconnect_t',
- { 'domid' : self.dom,
- 'blkif_handle' : 0 })
- self.factory.writeRequest(msg, response=response)
+ def recv_fe_interface_connect(self, msg, req):
+ val = unpackMsg('blkif_fe_interface_connect_t', msg)
+ handle = val['handle']
+ backend = self.getBackendInterfaceByHandle(handle)
+ if backend:
+ backend.connectInterface(val)
+ else:
+ log.error('interface connect on unknown interface: handle=%d', handle)
- def send_be_destroy(self, response=None):
- log.debug('>BlkifController>send_be_destroy> dom=%d', self.dom)
- msg = packMsg('blkif_be_destroy_t',
- { 'domid' : self.dom,
- 'blkif_handle' : 0 })
- self.factory.writeRequest(msg, response=response)
- def send_be_vbd_create(self, vdev, response=None):
- dev = self.devices[vdev]
- msg = packMsg('blkif_be_vbd_create_t',
- { 'domid' : self.dom,
- 'blkif_handle' : 0,
- 'vdevice' : dev.vdev,
- 'readonly' : dev.readonly() })
- self.factory.writeRequest(msg, response=response)
-
- def send_be_vbd_grow(self, vdev, response=None):
- dev = self.devices[vdev]
- msg = packMsg('blkif_be_vbd_grow_t',
- { 'domid' : self.dom,
- 'blkif_handle' : 0,
- 'vdevice' : dev.vdev,
- 'extent.device' : dev.device,
- 'extent.sector_start' : dev.start_sector,
- 'extent.sector_length' : dev.nr_sectors })
- self.factory.writeRequest(msg, response=response)
-
- def send_be_vbd_destroy(self, vdev, response=None):
- log.debug('>BlkifController>send_be_vbd_destroy> dom=%d vdev=%d', self.dom, vdev)
- dev = self.devices[vdev]
- msg = packMsg('blkif_be_vbd_destroy_t',
- { 'domid' : self.dom,
- 'blkif_handle' : 0,
- 'vdevice' : dev.vdev })
- del self.devices[vdev]
- self.factory.writeRequest(msg, response=response)
+
diff --git a/tools/python/xen/xend/server/channel.py b/tools/python/xen/xend/server/channel.py
index d78ba252cb..6dfebe37be 100755
--- a/tools/python/xen/xend/server/channel.py
+++ b/tools/python/xen/xend/server/channel.py
@@ -45,7 +45,7 @@ class ChannelFactory:
del self.channels[idx]
self.notifier.unbind(idx)
- def domChannel(self, dom):
+ def domChannel(self, dom, local_port=0, remote_port=0):
"""Get the channel for the given domain.
Construct if necessary.
@@ -55,7 +55,8 @@ class ChannelFactory:
"""
chan = self.getDomChannel(dom)
if not chan:
- chan = Channel(self, dom)
+ chan = Channel(self, dom, local_port=local_port,
+ remote_port=remote_port)
self.addChannel(chan)
return chan
@@ -91,10 +92,25 @@ class ChannelFactory:
"""
self.delChannel(channel.idx)
- def createPort(self, dom):
+ def createPort(self, dom, local_port=0, remote_port=0):
"""Create a port for a channel to the given domain.
+ If only the domain is specified, a new channel with new port ids is
+ created. If one port id is specified and the given port id is in use,
+ the other port id is filled. If one port id is specified and the
+ given port id is not in use, a new channel is created with one port
+ id equal to the given id and a new id for the other end. If both
+ port ids are specified, a port is reconnected using the given port
+ ids.
+
+ @param dom: domain
+ @param local: local port id to use
+ @type local: int
+ @param remote: remote port id to use
+ @type remote: int
+ @return: port object
"""
- return xu.port(dom)
+ return xu.port(dom, local_port=int(local_port),
+ remote_port=int(remote_port))
def channelFactory():
"""Singleton constructor for the channel factory.
@@ -155,8 +171,10 @@ class VirqChannel(BaseChannel):
"""
BaseChannel.__init__(self, factory)
self.virq = virq
+ self.factory = factory
# Notification port (int).
- self.port = xc.evtchn_bind_virq(virq)
+ #self.port = xc.evtchn_bind_virq(virq)
+ self.port = factory.notifier.bind_virq(virq)
self.idx = self.port
# Clients to call when a virq arrives.
self.clients = []
@@ -192,7 +210,8 @@ class VirqChannel(BaseChannel):
c.virqReceived(self.virq)
def notify(self):
- xc.evtchn_send(self.port)
+ # xc.evtchn_send(self.port)
+ self.factory.notifier.virq_send(self.port)
class Channel(BaseChannel):
@@ -200,7 +219,7 @@ class Channel(BaseChannel):
are multiplexed over the channel (console, block devs, net devs).
"""
- def __init__(self, factory, dom):
+ def __init__(self, factory, dom, local_port=0, remote_port=0):
"""Create a channel to the given domain using the given factory.
Do not call directly, use domChannel on the factory.
@@ -209,7 +228,8 @@ class Channel(BaseChannel):
# Domain.
self.dom = int(dom)
# Domain port (object).
- self.port = self.factory.createPort(dom)
+ self.port = self.factory.createPort(dom, local_port=local_port,
+ remote_port=remote_port)
# Channel port (int).
self.idx = self.port.local_port
# Registered devices.
@@ -262,6 +282,7 @@ class Channel(BaseChannel):
self.devs.append(dev)
for ty in types:
self.devs_by_type[ty] = dev
+ self.port.register(ty)
def deregisterDevice(self, dev):
"""Remove the registration for a device controller.
@@ -273,6 +294,7 @@ class Channel(BaseChannel):
types = [ ty for (ty, d) in self.devs_by_type.items() if d == dev ]
for ty in types:
del self.devs_by_type[ty]
+ self.port.deregister(ty)
def getDevice(self, type):
"""Get the device controller handling a message type.
@@ -333,13 +355,15 @@ class Channel(BaseChannel):
(ty, subty) = self.getMessageType(msg)
#todo: Must respond before writing any more messages.
#todo: Should automate this (respond on write)
- self.port.write_response(msg)
+ responded = 0
dev = self.getDevice(ty)
if dev:
- dev.requestReceived(msg, ty, subty)
+ responded = dev.requestReceived(msg, ty, subty)
else:
print ("requestReceived> No device: Message type %s %d:%d"
% (msgTypeName(ty, subty), ty, subty)), self
+ if not responded:
+ self.port.write_response(msg)
def handleResponses(self):
work = 0
diff --git a/tools/python/xen/xend/server/console.py b/tools/python/xen/xend/server/console.py
index 9221600bdb..ea4bf49921 100755
--- a/tools/python/xen/xend/server/console.py
+++ b/tools/python/xen/xend/server/console.py
@@ -48,7 +48,6 @@ class ConsoleProtocol(protocol.Protocol):
self.loseConnection()
def write(self, data):
- #if not self.connected: return -1
self.transport.write(data)
return len(data)
@@ -81,14 +80,14 @@ class ConsoleControllerFactory(controller.ControllerFactory):
"""Factory for creating console controllers.
"""
- def createInstance(self, dom, console_port=None):
+ def createController(self, dom, console_port=None):
if console_port is None:
console_port = CONSOLE_PORT_BASE + dom
- for c in self.getInstances():
+ for c in self.getControllers():
if c.console_port == console_port:
raise XendError('console port in use: ' + str(console_port))
console = ConsoleController(self, dom, console_port)
- self.addInstance(console)
+ self.addController(console)
log.info("Created console id=%s domain=%d port=%d",
console.idx, console.dom, console.console_port)
eserver.inject('xend.console.create',
@@ -98,7 +97,7 @@ class ConsoleControllerFactory(controller.ControllerFactory):
def consoleClosed(self, console):
log.info("Closed console id=%s", console.idx)
eserver.inject('xend.console.close', console.idx)
- self.delInstance(console)
+ self.delController(console)
class ConsoleController(controller.Controller):
"""Console controller for a domain.
@@ -113,7 +112,7 @@ class ConsoleController(controller.Controller):
def __init__(self, factory, dom, console_port):
controller.Controller.__init__(self, factory, dom)
- self.majorTypes = [ CMSG_CONSOLE ]
+ self.addMethod(CMSG_CONSOLE, 0, None)
self.status = self.STATUS_NEW
self.addr = None
self.conn = None
diff --git a/tools/python/xen/xend/server/controller.py b/tools/python/xen/xend/server/controller.py
index fdc8bfc0ac..c8962d4675 100755
--- a/tools/python/xen/xend/server/controller.py
+++ b/tools/python/xen/xend/server/controller.py
@@ -56,9 +56,7 @@ class CtrlMsgRcvr:
@ivar dom: the domain we are a control interface for
@type dom: int
@ivar majorTypes: major message types we are interested in
- @type majorTypes: [int]
- @ivar subTypes: mapping of message subtypes to methods
- @ivar subTypes: {int:method}
+ @type majorTypes: {int:{int:method}}
@ivar timeout: timeout (in seconds) for message handlers
@type timeout: int
@@ -72,8 +70,7 @@ class CtrlMsgRcvr:
def __init__(self):
self.channelFactory = channel.channelFactory()
- self.majorTypes = [ ]
- self.subTypes = {}
+ self.majorTypes = {}
self.dom = None
self.channel = None
self.idx = None
@@ -83,6 +80,37 @@ class CtrlMsgRcvr:
def setTimeout(self, timeout):
self.timeout = timeout
+ def getMethod(self, type, subtype):
+ """Get the method for a type and subtype.
+
+ @param type: major message type
+ @param subtype: minor message type
+ @return: method or None
+ """
+ method = None
+ subtypes = self.majorTypes.get(type)
+ if subtypes:
+ method = subtypes.get(subtype)
+ return method
+
+ def addMethod(self, type, subtype, method):
+ """Add a method to handle a message type and subtype.
+
+ @param type: major message type
+ @param subtype: minor message type
+ @param method: method
+ """
+ subtypes = self.majorTypes.get(type)
+ if not subtypes:
+ subtypes = {}
+ self.majorTypes[type] = subtypes
+ subtypes[subtype] = method
+
+ def getMajorTypes(self):
+ """Get the list of major message types handled.
+ """
+ return self.majorTypes.keys()
+
def requestReceived(self, msg, type, subtype):
"""Dispatch a request message to handlers.
Called by the channel for requests with one of our types.
@@ -97,12 +125,14 @@ class CtrlMsgRcvr:
if DEBUG:
print 'requestReceived>',
printMsg(msg, all=1)
- method = self.subTypes.get(subtype)
+ responded = 0
+ method = self.getMethod(type, subtype)
if method:
- method(msg, 1)
+ responded = method(msg, 1)
elif DEBUG:
print ('requestReceived> No handler: Message type %s %d:%d'
% (msgTypeName(type, subtype), type, subtype)), self
+ return responded
def responseReceived(self, msg, type, subtype):
"""Dispatch a response to handlers.
@@ -125,7 +155,7 @@ class CtrlMsgRcvr:
printMsg(msg, all=1)
if self.callResponders(msg):
return
- method = self.subTypes.get(subtype)
+ method = self.getMethod(type, subtype)
if method:
method(msg, 0)
elif DEBUG:
@@ -190,7 +220,7 @@ class CtrlMsgRcvr:
self.channel = self.channelFactory.domChannel(self.dom)
self.idx = self.channel.getIndex()
if self.majorTypes:
- self.channel.registerDevice(self.majorTypes, self)
+ self.channel.registerDevice(self.getMajorTypes(), self)
def deregisterChannel(self):
"""Deregister interest in our major message types with the
@@ -242,67 +272,94 @@ class CtrlMsgRcvr:
else:
print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self
-class ControllerFactory(CtrlMsgRcvr):
+class ControllerFactory:
"""Abstract class for factories creating controllers for a domain.
- Maintains a table of instances.
+ Maintains a table of controllers.
- @ivar instances: mapping of index to controller instance
- @type instances: {int: Controller}
+ @ivar controllers: mapping of index to controller instance
+ @type controllers: {String: Controller}
@ivar dom: domain
@type dom: int
"""
def __init__(self):
- CtrlMsgRcvr.__init__(self)
- self.instances = {}
- self.dom = 0
+ self.controllers = {}
- def addInstance(self, instance):
+ def addController(self, controller):
"""Add a controller instance (under its index).
"""
- self.instances[instance.idx] = instance
+ self.controllers[controller.idx] = controller
- def getInstance(self, idx):
- """Get a controller instance from its index.
+ def getControllers(self):
+ """Get a list of all controllers.
"""
- return self.instances.get(idx)
+ return self.controllers.values()
- def getInstances(self):
- """Get a list of all controller instances.
+ def getControllerByIndex(self, idx):
+ """Get a controller from its index.
"""
- return self.instances.values()
+ return self.controllers.get(idx)
- def getInstanceByDom(self, dom):
- """Get the controller instance for the given domain.
+ def getControllerByDom(self, dom):
+ """Get the controller for the given domain.
+
+ @param dom: domain id
+ @type dom: int
+ @return: controller or None
"""
- for inst in self.instances.values():
+ for inst in self.controllers.values():
if inst.dom == dom:
return inst
return None
- def delInstance(self, instance):
- """Delete an instance from the table.
- """
- if instance.idx in self.instances:
- del self.instances[instance.idx]
+ def getController(self, dom):
+ """Create or find the controller for a domain.
- def createInstance(self, dom, recreate=0):
- """Create an instance. Define in a subclass.
+ @param dom: domain
+ @return: controller
+ """
+ ctrl = self.getControllerByDom(dom)
+ if ctrl is None:
+ ctrl = self.createController(dom)
+ self.addController(ctrl)
+ return ctrl
+
+ def createController(self, dom):
+ """Create a controller. Define in a subclass.
@param dom: domain
@type dom: int
- @param recreate: true if the instance is being recreated (after xend restart)
- @type recreate: int
+ @return: controller instance
+ @rtype: Controller (or subclass)
"""
raise NotImplementedError()
- def instanceClosed(self, instance):
- """Callback called when an instance is closed (usually by the instance).
+ def delController(self, controller):
+ """Delete a controller instance from the table.
+
+ @param controller: controller instance
+ """
+ if controller.idx in self.controllers:
+ del self.controllers[controller.idx]
+
+ def controllerClosed(self, controller):
+ """Callback called when a controller is closed (usually by the controller).
+
+ @param controller: controller instance
"""
- self.delInstance(instance)
+ self.delController(controller)
class Controller(CtrlMsgRcvr):
"""Abstract class for a device controller attached to a domain.
+
+ @ivar factory: controller factory
+ @type factory: ControllerFactory
+ @ivar dom: domain
+ @type dom: int
+ @ivar channel: channel to the domain
+ @type channel: Channel
+ @ivar idx: channel index
+ @type idx: String
"""
def __init__(self, factory, dom):
@@ -321,10 +378,271 @@ class Controller(CtrlMsgRcvr):
"""The controller channel has been lost.
"""
self.deregisterChannel()
- self.factory.instanceClosed(self)
+ self.factory.controllerClosed(self)
+
+class SplitControllerFactory(ControllerFactory):
+ """Abstract class for factories creating split controllers for a domain.
+ Maintains a table of backend controllers.
+ """
+
+ def __init__(self):
+ ControllerFactory.__init__(self)
+ self.backendControllers = {}
+
+ def getBackendControllers(self):
+ return self.backendControllers.values()
+ def getBackendControllerByDomain(self, dom):
+ """Get the backend controller for a domain if there is one.
+
+ @param dom: backend domain
+ @return: backend controller
+ """
+ return self.backendControllers.get(dom)
+
+ def getBackendController(self, dom):
+ """Get the backend controller for a domain, creating
+ if necessary.
+
+ @param dom: backend domain
+ @return: backend controller
+ """
+ b = self.getBackendControllerByDomain(dom)
+ if b is None:
+ b = self.createBackendController(dom)
+ self.backendControllers[b.dom] = b
+ return b
+
+ def createBackendController(self, dom):
+ """Create a backend controller. Define in a subclass.
+
+ @param dom: backend domain
+ @return: backend controller
+ """
+ raise NotImplementedError()
+
+ def delBackendController(self, ctrlr):
+ """Remove a backend controller.
+
+ @param ctrlr: backend controller
+ """
+ if ctrlr.dom in self.backendControllers:
+ del self.backendControllers[ctrlr.dom]
+
+ def backendControllerClosed(self, ctrlr):
+ """Callback called when a backend is closed.
+ """
+ self.delBackendController(ctrlr)
+
+ def createBackendInterface(self, ctrl, dom, handle):
+ """Create a backend interface. Define in a subclass.
+
+ @param ctrl: frontend controller
+ @param dom: backend domain
+ @return: backend interface
+ """
+ raise NotImplementedError()
+
+class BackendController(Controller):
+ """Abstract class for a backend device controller attached to a domain.
+
+ @ivar factory: backend controller factory
+ @type factory: BackendControllerFactory
+ @ivar dom: backend domain
+ @type dom: int
+ @ivar channel: channel to the domain
+ @type channel: Channel
+ """
+
+
+ def __init__(self, factory, dom):
+ CtrlMsgRcvr.__init__(self)
+ self.factory = factory
+ self.dom = int(dom)
+ self.channel = None
+ self.backendInterfaces = {}
+
+ def close(self):
+ self.lostChannel()
+
+ def lostChannel(self):
+ self.deregisterChannel()
+ self.backend.backendClosed(self)
+
+ def registerInterface(self, intf):
+ key = intf.getInterfaceKey()
+ self.backendInterfaces[key] = intf
+
+ def deregisterInterface(self, intf):
+ key = intf.getInterfaceKey()
+ if key in self.backendInterfaces:
+ del self.backendInterfaces[key]
+
+ def getInterface(self, dom, handle):
+ key = (dom, handle)
+ return self.backendInterfaces.get(key)
+
+
+ def createBackendInterface(self, ctrl, dom, handle):
+ """Create a backend interface. Define in a subclass.
+
+ @param ctrl: controller
+ @param dom: backend domain
+ @param handle: backend handle
+ """
+ raise NotImplementedError()
+
+
+class BackendInterface:
+ """Abstract class for a domain's interface onto a backend controller.
+ """
+
+ def __init__(self, controller, dom, handle):
+ """
+
+ @param controller: front-end controller
+ @param dom: back-end domain
+ @param handle: back-end interface handle
+ """
+ self.factory = controller.factory
+ self.controller = controller
+ self.dom = int(dom)
+ self.handle = handle
+ self.backend = self.getBackendController()
+
+ def registerInterface(self):
+ self.backend.registerInterface(self)
+
+ def getInterfaceKey(self):
+ return (self.controller.dom, self.handle)
+
+ def getBackendController(self):
+ return self.factory.getBackendController(self.dom)
+
+ def writeRequest(self, msg, response=None):
+ return self.backend.writeRequest(msg, response=response)
+
+ def writeResponse(self, msg):
+ return self.backend.writeResponse(msg)
+
+ def close(self):
+ self.backend.deregisterInterface(self)
+ self.controller.backendInterfaceClosed(self)
+
+class SplitController(Controller):
+ """Abstract class for a device controller attached to a domain.
+ A SplitController manages a BackendInterface for each backend domain
+ it has at least one device for.
+ """
+
+ def __init__(self, factory, dom):
+ Controller.__init__(self, factory, dom)
+ self.backendInterfaces = {}
+ self.backendHandle = 0
+ self.devices = {}
+
+ def getDevices(self):
+ """Get a list of the devices..
+ """
+ return self.devices.values()
+
+ def delDevice(self, idx):
+ """Remove the device with the given index from the device table.
+
+ @param idx device index
+ """
+ if idx in self.devices:
+ del self.devices[idx]
+
+ def getDevice(self, idx):
+ """Get the device with a given index.
+
+ @param idx device index
+ @return device (or None)
+ """
+ return self.devices.get(idx)
+
+ def findDevice(self, idx):
+ """Find a device. If idx is non-negative,
+ get the device with the given index. If idx is negative,
+ look for the device with least index greater than -idx - 2.
+ For example, if idx is -2, look for devices with index
+ greater than 0, i.e. 1 or above.
+
+ @param idx device index
+ @return device (or None)
+ """
+ if idx < 0:
+ idx = -idx - 2
+ val = None
+ for dev in self.devices.values():
+ if dev.idx <= idx: continue
+ if (val is None) or (dev.idx < val.idx):
+ val = dev
+ else:
+ val = getDevice(idx)
+ return val
+
+ def getMaxDeviceIdx(self):
+ """Get the maximum id used by devices.
+
+ @return maximum idx
+ """
+ maxIdx = 0
+ for dev in self.devices:
+ if dev.idx > maxIdx:
+ maxIdx = dev.idx
+ return maxIdx
+
+ def getBackendInterfaces(self):
+ return self.backendInterfaces.values()
+
+ def getBackendInterfaceByHandle(self, handle):
+ for b in self.getBackendInterfaces():
+ if b.handle == handle:
+ return b
+ return None
+
+ def getBackendInterfaceByDomain(self, dom):
+ return self.backendInterfaces.get(dom)
+
+ def getBackendInterface(self, dom):
+ """Get the backend interface for a domain.
+
+ @param dom: domain
+ @return: backend controller
+ """
+ b = self.getBackendInterfaceByDomain(dom)
+ if b is None:
+ handle = self.backendHandle
+ self.backendHandle += 1
+ b = self.factory.createBackendInterface(self, dom, handle)
+ b.registerInterface()
+ self.backendInterfaces[b.dom] = b
+ return b
+
+ def delBackendInterface(self, ctrlr):
+ """Remove a backend controller.
+
+ @param ctrlr: backend controller
+ """
+ if ctrlr.dom in self.backendInterfaces:
+ del self.backendInterfaces[ctrlr.dom]
+
+ def backendInterfaceClosed(self, ctrlr):
+ """Callback called when a backend is closed.
+ """
+ self.delBackendInterface(ctrlr)
+
class Dev:
"""Abstract class for a device attached to a device controller.
+
+ @ivar idx: identifier
+ @type idx: String
+ @ivar controller: device controller
+ @type controller: DeviceController
+ @ivar props: property table
+ @type props: { String: value }
"""
def __init__(self, idx, controller):
@@ -349,6 +667,32 @@ class Dev:
del self.props[k]
def sxpr(self):
+ """Get the s-expression for the deivice.
+ Implement in a subclass.
+
+ @return: sxpr
+ """
+ raise NotImplementedError()
+
+ def configure(self, config, change=0):
raise NotImplementedError()
+class SplitDev(Dev):
+
+ def __init__(self, idx, controller):
+ Dev.__init__(self, idx, controller)
+ self.backendDomain = 0
+ self.index = None
+
+ def getBackendInterface(self):
+ return self.controller.getBackendInterface(self.backendDomain)
+
+ def getIndex(self):
+ return self.index
+
+ def setIndex(self, index):
+ self.index = index
+
+
+
diff --git a/tools/python/xen/xend/server/domain.py b/tools/python/xen/xend/server/domain.py
index bb8932d6ac..eb0dbcf48b 100644
--- a/tools/python/xen/xend/server/domain.py
+++ b/tools/python/xen/xend/server/domain.py
@@ -10,30 +10,14 @@ class DomainControllerFactory(controller.ControllerFactory):
"""Factory for creating domain controllers.
"""
- def createInstance(self, dom):
+ def createController(self, dom):
"""Create a domain controller.
dom domain
returns domain controller
"""
- d = DomainController(self, dom)
- self.addInstance(d)
- return d
-
- def getInstanceByDom(self, dom):
- """Get a domain controller for a domain, creating if necessary.
-
- dom domain
-
- returns domain controller
- """
- for inst in self.instances.values():
- if inst.dom == dom:
- return inst
- inst = self.createInstance(dom)
- return inst
-
+ return DomainController(self, dom)
class DomainController(controller.Controller):
"""Generic controller for a domain.
@@ -44,20 +28,31 @@ class DomainController(controller.Controller):
"""
reasons = {'poweroff' : 'shutdown_poweroff_t',
'reboot' : 'shutdown_reboot_t',
- 'suspend' : 'shutdown_suspend_t' }
+ 'suspend' : 'shutdown_suspend_t',
+ 'sysrq' : 'shutdown_sysrq_t' }
def __init__(self, factory, dom):
controller.Controller.__init__(self, factory, dom)
- self.majorTypes = [ CMSG_SHUTDOWN ]
+ self.addMethod(CMSG_SHUTDOWN, 0, None)
+ self.addMethod(CMSG_MEM_REQUEST, 0, None)
self.registerChannel()
- def shutdown(self, reason):
+ def shutdown(self, reason, key=0):
"""Shutdown a domain.
reason shutdown reason
+ key sysrq key (only if reason is 'sysrq')
"""
msgtype = self.reasons.get(reason)
if not msgtype:
raise XendError('invalid reason:' + reason)
- msg = packMsg(msgtype, {})
+ extra = {}
+ if reason == 'sysrq': extra['key'] = key
+ print extra
+ self.writeRequest(packMsg(msgtype, extra))
+
+ def mem_target_set(self, target):
+ """Set domain memory target in pages.
+ """
+ msg = packMsg('mem_request_t', { 'target' : target * (1 << 8)} )
self.writeRequest(msg)
diff --git a/tools/python/xen/xend/server/messages.py b/tools/python/xen/xend/server/messages.py
index 92b26091b2..4f9f9119a2 100644
--- a/tools/python/xen/xend/server/messages.py
+++ b/tools/python/xen/xend/server/messages.py
@@ -1,5 +1,6 @@
import sys
import struct
+import types
from xen.lowlevel import xu
@@ -28,69 +29,106 @@ msg_formats.update(console_formats)
CMSG_BLKIF_BE = 1
CMSG_BLKIF_FE = 2
-CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0
-CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32
-CMSG_BLKIF_FE_INTERFACE_CONNECT = 33
-CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34
-
-CMSG_BLKIF_BE_CREATE = 0
-CMSG_BLKIF_BE_DESTROY = 1
-CMSG_BLKIF_BE_CONNECT = 2
-CMSG_BLKIF_BE_DISCONNECT = 3
-CMSG_BLKIF_BE_VBD_CREATE = 4
-CMSG_BLKIF_BE_VBD_DESTROY = 5
-CMSG_BLKIF_BE_VBD_GROW = 6
-CMSG_BLKIF_BE_VBD_SHRINK = 7
-CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32
-
-BLKIF_DRIVER_STATUS_DOWN = 0
-BLKIF_DRIVER_STATUS_UP = 1
-
-BLKIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
-BLKIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
-BLKIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
-
-BLKIF_BE_STATUS_OKAY = 0
-BLKIF_BE_STATUS_ERROR = 1
-BLKIF_BE_STATUS_INTERFACE_EXISTS = 2
-BLKIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
-BLKIF_BE_STATUS_INTERFACE_CONNECTED = 4
-BLKIF_BE_STATUS_VBD_EXISTS = 5
-BLKIF_BE_STATUS_VBD_NOT_FOUND = 6
-BLKIF_BE_STATUS_OUT_OF_MEMORY = 7
-BLKIF_BE_STATUS_EXTENT_NOT_FOUND = 8
-BLKIF_BE_STATUS_MAPPING_ERROR = 9
+CMSG_BLKIF_FE_INTERFACE_STATUS = 0
+CMSG_BLKIF_FE_DRIVER_STATUS = 32
+CMSG_BLKIF_FE_INTERFACE_CONNECT = 33
+CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34
+CMSG_BLKIF_FE_INTERFACE_QUERY = 35
+
+CMSG_BLKIF_BE_CREATE = 0
+CMSG_BLKIF_BE_DESTROY = 1
+CMSG_BLKIF_BE_CONNECT = 2
+CMSG_BLKIF_BE_DISCONNECT = 3
+CMSG_BLKIF_BE_VBD_CREATE = 4
+CMSG_BLKIF_BE_VBD_DESTROY = 5
+CMSG_BLKIF_BE_VBD_GROW = 6
+CMSG_BLKIF_BE_VBD_SHRINK = 7
+CMSG_BLKIF_BE_DRIVER_STATUS = 32
+
+BLKIF_DRIVER_STATUS_DOWN = 0
+BLKIF_DRIVER_STATUS_UP = 1
+
+BLKIF_INTERFACE_STATUS_CLOSED = 0 #/* Interface doesn't exist. */
+BLKIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
+BLKIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
+BLKIF_INTERFACE_STATUS_CHANGED = 3 #/* A device has been added or removed. */
+
+BLKIF_BE_STATUS_OKAY = 0
+BLKIF_BE_STATUS_ERROR = 1
+BLKIF_BE_STATUS_INTERFACE_EXISTS = 2
+BLKIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
+BLKIF_BE_STATUS_INTERFACE_CONNECTED = 4
+BLKIF_BE_STATUS_VBD_EXISTS = 5
+BLKIF_BE_STATUS_VBD_NOT_FOUND = 6
+BLKIF_BE_STATUS_OUT_OF_MEMORY = 7
+BLKIF_BE_STATUS_EXTENT_NOT_FOUND = 8
+BLKIF_BE_STATUS_MAPPING_ERROR = 9
blkif_formats = {
'blkif_be_connect_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT),
+ # Connect be to fe (in response to blkif_fe_interface_connect_t).
'blkif_be_create_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE),
+ # Create be.
'blkif_be_disconnect_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DISCONNECT),
+ # Disconnect be from fe.
'blkif_be_destroy_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY),
+ # Destroy be (after disconnect).
+ # Make be do this even if no disconnect (and destroy all vbd too).
'blkif_be_vbd_create_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE),
+ # Create a vbd device.
'blkif_be_vbd_grow_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW),
+ # Change the size of a vbd device. Remove?
+ # Do in one go in blkif_be_vbd_create_t.
'blkif_be_vbd_destroy_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_DESTROY),
-
- 'blkif_fe_interface_status_changed_t':
- (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED),
-
- 'blkif_fe_driver_status_changed_t':
- (CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED),
+ # Destroy a vbd.
+
+ # Add message to query be for state and vbds.
+
+ 'blkif_fe_interface_status_t':
+ (CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS),
+ # Notify device status to fe.
+ # Also used to notify 'any' device change with status BLKIF_INTERFACE_STATUS_CHANGED.
+ # Rename to blkif_fe_interface_status.
+
+ 'blkif_fe_driver_status_t':
+ (CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS),
+ # Comes from fe, treated as notifying that fe has come up/changed.
+ # Xend sets be(s) to BLKIF_INTERFACE_STATUS_DISCONNECTED,
+ # sends blkif_fe_interface_status_t to fe (from each be).
+ #
+ # Rename to blkif_fe_driver_status.
+ # Reply with i/f count.
+ # The i/f sends probes (using -ve trick), we reply with the info.
'blkif_fe_interface_connect_t':
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT),
+ # Comes from fe, passing shmem frame to use for be.
+ # fe sends when gets blkif_fe_interface_status_t with state NETIF_INTERFACE_STATUS_DISCONNECTED.
+ # Xend creates event channel and notifies be.
+ # Then notifies fe of event channel with blkif_fe_interface_status_t.
+
+ # Add message to kick fe to probe for devices.
+ # Just report new devices to fe?
+
+ #
+ # Add message for fe to probe a device.
+ # And probing with id -1 should return first.
+ # And probing with id -n should return first device with id > n.
+
+ # Add message to query fe for state and vbds.
}
msg_formats.update(blkif_formats)
@@ -102,23 +140,25 @@ msg_formats.update(blkif_formats)
CMSG_NETIF_BE = 3
CMSG_NETIF_FE = 4
-CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0
-CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32
-CMSG_NETIF_FE_INTERFACE_CONNECT = 33
-CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34
+CMSG_NETIF_FE_INTERFACE_STATUS = 0
+CMSG_NETIF_FE_DRIVER_STATUS = 32
+CMSG_NETIF_FE_INTERFACE_CONNECT = 33
+CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34
+CMSG_NETIF_FE_INTERFACE_QUERY = 35
-CMSG_NETIF_BE_CREATE = 0
-CMSG_NETIF_BE_DESTROY = 1
-CMSG_NETIF_BE_CONNECT = 2
-CMSG_NETIF_BE_DISCONNECT = 3
-CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32
+CMSG_NETIF_BE_CREATE = 0
+CMSG_NETIF_BE_DESTROY = 1
+CMSG_NETIF_BE_CONNECT = 2
+CMSG_NETIF_BE_DISCONNECT = 3
+CMSG_NETIF_BE_DRIVER_STATUS = 32
-NETIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
-NETIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
-NETIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
+NETIF_INTERFACE_STATUS_CLOSED = 0 #/* Interface doesn't exist. */
+NETIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
+NETIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
+NETIF_INTERFACE_STATUS_CHANGED = 3 #/* A device has been added or removed. */
-NETIF_DRIVER_STATUS_DOWN = 0
-NETIF_DRIVER_STATUS_UP = 1
+NETIF_DRIVER_STATUS_DOWN = 0
+NETIF_DRIVER_STATUS_UP = 1
netif_formats = {
'netif_be_connect_t':
@@ -133,22 +173,86 @@ netif_formats = {
'netif_be_destroy_t':
(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY),
- 'netif_be_driver_status_changed_t':
- (CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED),
+ 'netif_be_driver_status_t':
+ (CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS),
- 'netif_fe_driver_status_changed_t':
- (CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED),
+ 'netif_fe_driver_status_t':
+ (CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS),
'netif_fe_interface_connect_t':
(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT),
- 'netif_fe_interface_status_changed_t':
- (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED),
+ 'netif_fe_interface_status_t':
+ (CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS),
}
msg_formats.update(netif_formats)
#============================================================================
+# USB interface message types.
+#============================================================================
+
+CMSG_USBIF_BE = 8
+CMSG_USBIF_FE = 9
+
+CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED = 0
+
+CMSG_USBIF_FE_DRIVER_STATUS_CHANGED = 32
+CMSG_USBIF_FE_INTERFACE_CONNECT = 33
+CMSG_USBIF_FE_INTERFACE_DISCONNECT = 34
+
+USBIF_DRIVER_STATUS_DOWN = 0
+USBIF_DRIVER_STATUS_UP = 1
+
+USBIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
+USBIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
+USBIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
+
+CMSG_USBIF_BE_CREATE = 0
+CMSG_USBIF_BE_DESTROY = 1
+CMSG_USBIF_BE_CONNECT = 2
+
+CMSG_USBIF_BE_DISCONNECT = 3
+CMSG_USBIF_BE_CLAIM_PORT = 4
+CMSG_USBIF_BE_RELEASE_PORT = 5
+
+CMSG_USBIF_BE_DRIVER_STATUS_CHANGED = 32
+
+USBIF_BE_STATUS_OKAY = 0
+USBIF_BE_STATUS_ERROR = 1
+
+USBIF_BE_STATUS_INTERFACE_EXISTS = 2
+USBIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
+USBIF_BE_STATUS_INTERFACE_CONNECTED = 4
+USBIF_BE_STATUS_OUT_OF_MEMORY = 7
+USBIF_BE_STATUS_MAPPING_ERROR = 9
+
+usbif_formats = {
+ 'usbif_be_create_t':
+ (CMSG_USBIF_BE, CMSG_USBIF_BE_CREATE),
+ 'usbif_be_destroy_t':
+ (CMSG_USBIF_BE, CMSG_USBIF_BE_DESTROY),
+ 'usbif_be_connect_t':
+ (CMSG_USBIF_BE, CMSG_USBIF_BE_CONNECT),
+ 'usbif_be_disconnect_t':
+ (CMSG_USBIF_BE, CMSG_USBIF_BE_DISCONNECT),
+ 'usbif_be_claim_port_t':
+ (CMSG_USBIF_BE, CMSG_USBIF_BE_CLAIM_PORT),
+ 'usbif_be_release_port_t':
+ (CMSG_USBIF_BE, CMSG_USBIF_BE_RELEASE_PORT),
+ 'usbif_fe_interface_status_changed_t':
+ (CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_STATUS_CHANGED),
+ 'usbif_fe_driver_status_changed_t':
+ (CMSG_USBIF_FE, CMSG_USBIF_FE_DRIVER_STATUS_CHANGED),
+ 'usbif_fe_interface_connect_t':
+ (CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_CONNECT),
+ 'usbif_fe_interface_disconnect_t':
+ (CMSG_USBIF_FE, CMSG_USBIF_FE_INTERFACE_DISCONNECT)
+ }
+
+msg_formats.update(usbif_formats)
+
+#============================================================================
# Domain shutdown message types.
#============================================================================
@@ -157,10 +261,12 @@ CMSG_SHUTDOWN = 6
CMSG_SHUTDOWN_POWEROFF = 0
CMSG_SHUTDOWN_REBOOT = 1
CMSG_SHUTDOWN_SUSPEND = 2
+CMSG_SHUTDOWN_SYSRQ = 3
STOPCODE_shutdown = 0
STOPCODE_reboot = 1
STOPCODE_suspend = 2
+STOPCODE_sysrq = 3
shutdown_formats = {
'shutdown_poweroff_t':
@@ -171,12 +277,28 @@ shutdown_formats = {
'shutdown_suspend_t':
(CMSG_SHUTDOWN, CMSG_SHUTDOWN_SUSPEND),
+
+ 'shutdown_sysrq_t':
+ (CMSG_SHUTDOWN, CMSG_SHUTDOWN_SYSRQ)
}
msg_formats.update(shutdown_formats)
#============================================================================
+# Domain memory reservation message.
+#============================================================================
+
+CMSG_MEM_REQUEST = 7
+CMSG_MEM_REQUEST_SET = 0
+
+mem_request_formats = {
+ 'mem_request_t':
+ (CMSG_MEM_REQUEST, CMSG_MEM_REQUEST_SET)
+ }
+
+msg_formats.update(mem_request_formats)
+#============================================================================
class Msg:
pass
@@ -208,16 +330,13 @@ def packMsg(ty, params):
(major, minor) = msg_formats[ty]
args = {}
for (k, v) in params.items():
- if k == 'mac':
+ if k in ['mac', 'be_mac']:
for i in range(0, 6):
- args['mac[%d]' % i] = v[i]
+ args['%s[%d]' % (k, i)] = v[i]
else:
args[k] = v
- if DEBUG:
- for (k, v) in args.items():
- print 'packMsg>', k, v, type(v)
msg = xu.message(major, minor, msgid, args)
- if DEBUG: print '<packMsg', msg.get_header()['id'], ty, params
+ if DEBUG: print '<packMsg', msg.get_header()['id'], ty, args
return msg
def unpackMsg(ty, msg):
@@ -233,19 +352,25 @@ def unpackMsg(ty, msg):
@rtype: dict
"""
args = msg.get_payload()
- mac = [0, 0, 0, 0, 0, 0]
- macs = []
- for (k, v) in args.items():
- if k.startswith('mac['):
- macs += k
- i = int(k[4:5])
- mac[i] = v
- else:
- pass
- if macs:
- args['mac'] = mac
- for k in macs:
- del args[k]
+ if DEBUG: print '>unpackMsg', args
+ if isinstance(args, types.StringType):
+ args = {'value': args}
+ else:
+ mac = [0, 0, 0, 0, 0, 0]
+ macs = []
+ for (k, v) in args.items():
+ if k.startswith('mac['):
+ macs.append(k)
+ i = int(k[4:5])
+ mac[i] = v
+ else:
+ pass
+ if macs:
+ args['mac'] = mac
+ print 'macs=', macs
+ print 'args=', args
+ for k in macs:
+ del args[k]
if DEBUG:
msgid = msg.get_header()['id']
print '<unpackMsg', msgid, ty, args
@@ -283,5 +408,5 @@ def printMsg(msg, out=sys.stdout, all=0):
ty = msgTypeName(major, minor)
print >>out, 'message:', 'type=', ty, '%d:%d' % (major, minor), 'id=%d' % msgid
if all:
- print >>out, 'payload=', unpackMsg(ty, msg)
+ print >>out, 'payload=', msg.get_payload()
diff --git a/tools/python/xen/xend/server/netif.py b/tools/python/xen/xend/server/netif.py
index 41be86ac99..00ad1f138b 100755
--- a/tools/python/xen/xend/server/netif.py
+++ b/tools/python/xen/xend/server/netif.py
@@ -1,142 +1,200 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+"""Support for virtual network interfaces.
+"""
import random
from twisted.internet import defer
-#defer.Deferred.debug = 1
from xen.xend import sxp
from xen.xend import Vifctl
from xen.xend.XendError import XendError
from xen.xend.XendLogging import log
from xen.xend import XendVnet
+from xen.xend.XendRoot import get_component
import channel
import controller
from messages import *
-class NetifControllerFactory(controller.ControllerFactory):
+class NetifBackendController(controller.BackendController):
+ """Handler for the 'back-end' channel to a network device driver domain.
+ """
+
+ def __init__(self, ctrl, dom):
+ controller.BackendController.__init__(self, ctrl, dom)
+ self.addMethod(CMSG_NETIF_BE,
+ CMSG_NETIF_BE_DRIVER_STATUS,
+ self.recv_be_driver_status)
+ self.registerChannel()
+
+ def recv_be_driver_status(self, msg, req):
+ val = unpackMsg('netif_be_driver_status_t', msg)
+ status = val['status']
+
+class NetifBackendInterface(controller.BackendInterface):
+ """Handler for the 'back-end' channel to a network device driver domain
+ on behalf of a front-end domain.
+
+ Each network device is handled separately, so we add no functionality
+ here.
+ """
+
+ pass
+
+class NetifControllerFactory(controller.SplitControllerFactory):
"""Factory for creating network interface controllers.
- Also handles the 'back-end' channel to the device driver domain.
"""
def __init__(self):
- controller.ControllerFactory.__init__(self)
+ controller.SplitControllerFactory.__init__(self)
- self.majorTypes = [ CMSG_NETIF_BE ]
+ def createController(self, dom):
+ """Create a network interface controller for a domain.
- self.subTypes = {
- #CMSG_NETIF_BE_CREATE : self.recv_be_create,
- #CMSG_NETIF_BE_CONNECT: self.recv_be_connect,
- CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
- }
- self.attached = 1
- self.registerChannel()
+ @param dom: domain
+ @return: netif controller
+ """
+ return NetifController(self, dom)
- def createInstance(self, dom, recreate=0):
- """Create or find the network interface controller for a domain.
+ def createBackendController(self, dom):
+ """Create a network device backend controller.
- dom domain
- recreate if true this is a recreate (xend restarted)
+ @param dom: backend domain
+ @return: backend controller
+ """
+ return NetifBackendController(self, dom)
+
+ def createBackendInterface(self, ctrl, dom, handle):
+ """Create a network device backend interface.
- returns netif controller
+ @param ctrl: controller
+ @param dom: backend domain
+ @param handle: interface handle
+ @return: backend interface
"""
- netif = self.getInstanceByDom(dom)
- if netif is None:
- netif = NetifController(self, dom)
- self.addInstance(netif)
- return netif
+ return NetifBackendInterface(ctrl, dom, handle)
def getDomainDevices(self, dom):
- """Get the network device controllers for a domain.
+ """Get the network devices for a domain.
- dom domain
-
- returns netif controller
+ @param dom: domain
+ @return: netif controller list
"""
- netif = self.getInstanceByDom(dom)
+ netif = self.getControllerByDom(dom)
return (netif and netif.getDevices()) or []
def getDomainDevice(self, dom, vif):
"""Get a virtual network interface device for a domain.
- dom domain
- vif virtual interface index
-
- returns NetDev
+ @param dom: domain
+ @param vif: virtual interface index
+ @return: NetDev
"""
- netif = self.getInstanceByDom(dom)
+ netif = self.getControllerByDom(dom)
return (netif and netif.getDevice(vif)) or None
- def setControlDomain(self, dom, recreate=0):
- """Set the 'back-end' device driver domain.
-
- dom domain
- recreate if true this is a recreate (xend restarted)
- """
- if self.dom == dom: return
- self.deregisterChannel()
- if not recreate:
- self.attached = 0
- self.dom = dom
- self.registerChannel()
-
- def getControlDomain(self):
- """Get the domain id of the back-end control domain.
- """
- return self.dom
-
- def respond_be_connect(self, msg):
- val = unpackMsg('netif_be_connect_t', msg)
- dom = val['domid']
- vif = val['netif_handle']
- netif = self.getInstanceByDom(dom)
- if netif:
- netif.send_interface_connected(vif)
- else:
- log.warning("respond_be_connect> unknown dom=%d vif=%d", dom, vif)
- pass
-
- def recv_be_driver_status_changed(self, msg, req):
- val = unpackMsg('netif_be_driver_status_changed_t', msg)
- status = val['status']
- if status == NETIF_DRIVER_STATUS_UP and not self.attached:
- # If we are not attached the driver domain was changed, and
- # this signals the new driver domain is ready.
- for netif in self.getInstances():
- netif.reattach_devices()
- self.attached = 1
-
-class NetDev(controller.Dev):
+class NetDev(controller.SplitDev):
"""Info record for a network device.
"""
- def __init__(self, ctrl, vif, config):
- controller.Dev.__init__(self, vif, ctrl)
+ def __init__(self, vif, ctrl, config):
+ controller.SplitDev.__init__(self, vif, ctrl)
self.vif = vif
self.evtchn = None
self.configure(config)
+ self.status = NETIF_INTERFACE_STATUS_DISCONNECTED
+
+ def _get_config_mac(self, config):
+ vmac = sxp.child_value(config, 'mac')
+ if not vmac: return None
+ mac = [ int(x, 16) for x in vmac.split(':') ]
+ if len(mac) != 6: raise XendError("invalid mac: %s" % vmac)
+ return mac
+
+ def _get_config_be_mac(self, config):
+ vmac = sxp.child_value(config, 'be_mac')
+ if not vmac: return None
+ mac = [ int(x, 16) for x in vmac.split(':') ]
+ if len(mac) != 6: raise XendError("invalid backend mac: %s" % vmac)
+ return mac
+
+ def _get_config_ipaddr(self, config):
+ ips = sxp.children(config, elt='ip')
+ if ips:
+ val = []
+ for ipaddr in ips:
+ val.append(sxp.child0(ipaddr))
+ else:
+ val = None
+ return val
- def configure(self, config):
+ def configure(self, config, change=0):
+ if change:
+ return self.reconfigure(config)
self.config = config
self.mac = None
+ self.be_mac = None
self.bridge = None
self.script = None
self.ipaddr = []
-
- vmac = sxp.child_value(config, 'mac')
- if not vmac: raise XendError("invalid mac")
- mac = [ int(x, 16) for x in vmac.split(':') ]
- if len(mac) != 6: raise XendError("invalid mac")
- self.mac = mac
+ mac = self._get_config_mac(config)
+ if mac is None:
+ raise XendError("invalid mac")
+ self.mac = mac
+ self.be_mac = self._get_config_be_mac(config)
self.bridge = sxp.child_value(config, 'bridge')
self.script = sxp.child_value(config, 'script')
-
- ipaddrs = sxp.children(config, elt='ip')
- for ipaddr in ipaddrs:
- self.ipaddr.append(sxp.child0(ipaddr))
+ self.ipaddr = self._get_config_ipaddr(config) or []
+ try:
+ xd = get_component('xen.xend.XendDomain')
+ self.backendDomain = int(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
+ except:
+ raise XendError('invalid backend domain')
+
+ def reconfigure(self, config):
+ """Reconfigure the interface with new values.
+ Not all configuration parameters can be changed:
+ bridge, script and ip addresses can,
+ backend and mac cannot.
+
+ To leave a parameter unchanged, omit it from the changes.
+
+ @param config configuration changes
+ @return updated interface configuration
+ @raise XendError on errors
+ """
+ changes = {}
+ mac = self._get_config_mac(config)
+ be_mac = self._get_config_be_mac(config)
+ bridge = sxp.child_value(config, 'bridge')
+ script = sxp.child_value(config, 'script')
+ ipaddr = self._get_config_ipaddr(config)
+ xd = get_component('xen.xend.XendDomain')
+ backendDomain = str(xd.domain_lookup(sxp.child_value(config, 'backend', '0')).id)
+ if (mac is not None) and (mac != self.mac):
+ raise XendError("cannot change mac")
+ if (be_mac is not None) and (be_mac != self.be_mac):
+ raise XendError("cannot change backend mac")
+ if (backendDomain is not None) and (backendDomain != str(self.backendDomain)):
+ raise XendError("cannot change backend")
+ if (bridge is not None) and (bridge != self.bridge):
+ changes['bridge'] = bridge
+ if (script is not None) and (script != self.script):
+ changes['script'] = script
+ if (ipaddr is not None) and (ipaddr != self.ipaddr):
+ changes['ipaddr'] = ipaddr
+
+ if changes:
+ self.vifctl("down")
+ for (k, v) in changes.items():
+ setattr(self, k, v)
+ self.config = sxp.merge(config, self.config)
+ self.vifctl("up")
+ return self.config
+
def sxpr(self):
vif = str(self.vif)
mac = self.get_mac()
@@ -144,6 +202,8 @@ class NetDev(controller.Dev):
['idx', self.idx],
['vif', vif],
['mac', mac]]
+ if self.be_mac:
+ val.append(['be_mac', self.get_be_mac()])
if self.bridge:
val.append(['bridge', self.bridge])
if self.script:
@@ -154,6 +214,8 @@ class NetDev(controller.Dev):
val.append(['evtchn',
self.evtchn['port1'],
self.evtchn['port2']])
+ if self.index is not None:
+ val.append(['index', self.index])
return val
def get_vifname(self):
@@ -166,10 +228,23 @@ class NetDev(controller.Dev):
"""
return ':'.join(map(lambda x: "%02x" % x, self.mac))
+ def get_be_mac(self):
+ """Get the backend MAC address as a string.
+ """
+ return ':'.join(map(lambda x: "%02x" % x, self.be_mac))
+
def vifctl_params(self, vmname=None):
+ """Get the parameters to pass to vifctl.
+ """
dom = self.controller.dom
- name = vmname or ('DOM%d' % dom)
- return { 'domain': name,
+ if vmname is None:
+ xd = get_component('xen.xend.XendDomain')
+ try:
+ vm = xd.domain_lookup(dom)
+ vmname = vm.name
+ except:
+ vmname = 'DOM%d' % dom
+ return { 'domain': vmname,
'vif' : self.get_vifname(),
'mac' : self.get_mac(),
'bridge': self.bridge,
@@ -178,41 +253,133 @@ class NetDev(controller.Dev):
def vifctl(self, op, vmname=None):
"""Bring the device up or down.
+ The vmname is needed when bringing a device up for a new domain because
+ the domain is not yet in the table so we can't look its name up.
+
+ @param op: operation name (up, down)
+ @param vmname: vmname
"""
Vifctl.vifctl(op, **self.vifctl_params(vmname=vmname))
vnet = XendVnet.instance().vnet_of_bridge(self.bridge)
if vnet:
vnet.vifctl(op, self.get_vifname(), self.get_mac())
- def destroy(self):
+ def attach(self):
+ d = self.send_be_create()
+ d.addCallback(self.respond_be_create)
+ return d
+
+ def getEventChannelBackend(self):
+ val = 0
+ if self.evtchn:
+ val = self.evtchn['port1']
+ return val
+
+ def getEventChannelFrontend(self):
+ val = 0
+ if self.evtchn:
+ val = self.evtchn['port2']
+ return val
+
+ def send_be_create(self):
+ d = defer.Deferred()
+ msg = packMsg('netif_be_create_t',
+ { 'domid' : self.controller.dom,
+ 'netif_handle' : self.vif,
+ 'be_mac' : self.be_mac or [0, 0, 0, 0, 0, 0],
+ 'mac' : self.mac })
+ self.getBackendInterface().writeRequest(msg, response=d)
+ return d
+
+ def respond_be_create(self, msg):
+ val = unpackMsg('netif_be_create_t', msg)
+ return self
+
+ def destroy(self, change=0):
"""Destroy the device's resources and disconnect from the back-end
- device controller.
+ device controller. If 'change' is true notify the front-end interface.
+
+ @param change: change flag
"""
+ self.status = NETIF_INTERFACE_STATUS_CLOSED
def cb_destroy(val):
- self.controller.send_be_destroy(self.vif)
+ self.send_be_destroy()
+ self.getBackendInterface().close()
+ if change:
+ self.reportStatus()
log.debug("Destroying vif domain=%d vif=%d", self.controller.dom, self.vif)
self.vifctl('down')
- d = defer.Deferred()
+ d = self.send_be_disconnect()
d.addCallback(cb_destroy)
- self.controller.send_be_disconnect(self.vif, response=d)
+
+ def send_be_disconnect(self):
+ d = defer.Deferred()
+ msg = packMsg('netif_be_disconnect_t',
+ { 'domid' : self.controller.dom,
+ 'netif_handle' : self.vif })
+ self.getBackendInterface().writeRequest(msg, response=d)
+ return d
+
+ def send_be_destroy(self, response=None):
+ msg = packMsg('netif_be_destroy_t',
+ { 'domid' : self.controller.dom,
+ 'netif_handle' : self.vif })
+ self.controller.delDevice(self.vif)
+ self.getBackendInterface().writeRequest(msg, response=response)
+
+ def recv_fe_interface_connect(self, val, req):
+ if not req: return
+ self.evtchn = channel.eventChannel(self.backendDomain, self.controller.dom)
+ msg = packMsg('netif_be_connect_t',
+ { 'domid' : self.controller.dom,
+ 'netif_handle' : self.vif,
+ 'evtchn' : self.getEventChannelBackend(),
+ 'tx_shmem_frame' : val['tx_shmem_frame'],
+ 'rx_shmem_frame' : val['rx_shmem_frame'] })
+ d = defer.Deferred()
+ d.addCallback(self.respond_be_connect)
+ self.getBackendInterface().writeRequest(msg, response=d)
+ def respond_be_connect(self, msg):
+ val = unpackMsg('netif_be_connect_t', msg)
+ dom = val['domid']
+ vif = val['netif_handle']
+ self.status = NETIF_INTERFACE_STATUS_CONNECTED
+ self.reportStatus()
+
+ def reportStatus(self, resp=0):
+ msg = packMsg('netif_fe_interface_status_t',
+ { 'handle' : self.vif,
+ 'status' : self.status,
+ 'evtchn' : self.getEventChannelFrontend(),
+ 'domid' : self.backendDomain,
+ 'mac' : self.mac })
+ if resp:
+ self.controller.writeResponse(msg)
+ else:
+ self.controller.writeRequest(msg)
-class NetifController(controller.Controller):
+ def interfaceChanged(self):
+ """Notify the font-end that a device has been added or removed.
+ """
+ self.reportStatus()
+
+class NetifController(controller.SplitController):
"""Network interface controller. Handles all network devices for a domain.
"""
def __init__(self, factory, dom):
- controller.Controller.__init__(self, factory, dom)
+ controller.SplitController.__init__(self, factory, dom)
self.devices = {}
-
- self.majorTypes = [ CMSG_NETIF_FE ]
-
- self.subTypes = {
- CMSG_NETIF_FE_DRIVER_STATUS_CHANGED:
- self.recv_fe_driver_status_changed,
- CMSG_NETIF_FE_INTERFACE_CONNECT :
- self.recv_fe_interface_connect,
- }
+ self.addMethod(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_DRIVER_STATUS,
+ self.recv_fe_driver_status)
+ self.addMethod(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_INTERFACE_STATUS,
+ self.recv_fe_interface_status)
+ self.addMethod(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_INTERFACE_CONNECT,
+ self.recv_fe_interface_connect)
self.registerChannel()
def sxpr(self):
@@ -224,29 +391,16 @@ class NetifController(controller.Controller):
"""
controller.Controller.lostChannel(self)
- def getDevices(self):
- """Get a list of the devices.
- """
- return self.devices.values()
-
- def getDevice(self, vif):
- """Get a device.
-
- vif device index
-
- returns device (or None)
- """
- return self.devices.get(vif)
-
def addDevice(self, vif, config):
"""Add a network interface.
- vif device index
- config device configuration
-
- returns device
+ @param vif: device index
+ @param config: device configuration
+ @return: device
"""
- dev = NetDev(self, vif, config)
+ if vif in self.devices:
+ raise XendError('device exists:' + str(vif))
+ dev = NetDev(vif, self, config)
self.devices[vif] = dev
return dev
@@ -256,89 +410,84 @@ class NetifController(controller.Controller):
self.destroyDevices()
def destroyDevices(self):
+ """Destroy all devices.
+ """
for dev in self.getDevices():
dev.destroy()
def attachDevice(self, vif, config, recreate=0):
"""Attach a network device.
- If vmac is None a random mac address is assigned.
- @param vif interface index
- @param vmac mac address (string)
+ @param vif: interface index
+ @param config: device configuration
+ @param recreate: recreate flag (true after xend restart)
+ @return: deferred
"""
- self.addDevice(vif, config)
- d = defer.Deferred()
+ dev = self.addDevice(vif, config)
if recreate:
- d.callback(self)
+ d = defer.succeed(dev)
else:
- self.send_be_create(vif, response=d)
+ d = dev.attach()
return d
- def reattach_devices(self):
- """Reattach all devices when the back-end control domain has changed.
- """
- self.send_be_create(vif)
- self.attach_fe_devices()
-
- def attach_fe_devices(self):
- for dev in self.devices.values():
- msg = packMsg('netif_fe_interface_status_changed_t',
- { 'handle' : dev.vif,
- 'status' : NETIF_INTERFACE_STATUS_DISCONNECTED,
- 'evtchn' : 0,
- 'mac' : dev.mac })
- self.writeRequest(msg)
-
- def recv_fe_driver_status_changed(self, msg, req):
+ def recv_fe_driver_status(self, msg, req):
if not req: return
- msg = packMsg('netif_fe_driver_status_changed_t',
- { 'status' : NETIF_DRIVER_STATUS_UP,
- 'nr_interfaces' : len(self.devices) })
- self.writeRequest(msg)
- self.attach_fe_devices()
+ print
+ print 'recv_fe_driver_status>'
+ msg = packMsg('netif_fe_driver_status_t',
+ { 'status' : NETIF_DRIVER_STATUS_UP,
+ ## FIXME: max_handle should be max active interface id
+ 'max_handle' : len(self.devices)
+ #'max_handle' : self.getMaxDeviceIdx()
+ })
+ # Two ways of doing it:
+ # 1) front-end requests driver status, we reply with the interface count,
+ # front-end polls the interfaces,
+ # front-end checks they are all up
+ # 2) front-end requests driver status, we reply (with anything),
+ # we notify the interfaces,
+ # we notify driver status up with the count
+ # front-end checks they are all up
+ #
+ # We really want to use 1), but at the moment the xenU kernel panics
+ # in that mode, so we're sticking to 2) for now.
+ resp = 0
+ if resp:
+ self.writeResponse(msg)
+ else:
+ for dev in self.devices.values():
+ dev.reportStatus()
+ self.writeRequest(msg)
+ return resp
+ def recv_fe_interface_status(self, msg, req):
+ if not req: return
+ print
+ val = unpackMsg('netif_fe_interface_status_t', msg)
+ print "recv_fe_interface_status>", val
+ vif = val['handle']
+ dev = self.findDevice(vif)
+ if dev:
+ print 'recv_fe_interface_status>', 'dev=', dev
+ dev.reportStatus(resp=1)
+ else:
+ msg = packMsg('netif_fe_interface_status_t',
+ { 'handle' : -1,
+ 'status' : NETIF_INTERFACE_STATUS_CLOSED,
+ });
+ print 'recv_fe_interface_status>', 'no dev, returning -1'
+ self.writeResponse(msg)
+ return 1
+
+
def recv_fe_interface_connect(self, msg, req):
val = unpackMsg('netif_fe_interface_connect_t', msg)
- dev = self.devices[val['handle']]
- dev.evtchn = channel.eventChannel(0, self.dom)
- msg = packMsg('netif_be_connect_t',
- { 'domid' : self.dom,
- 'netif_handle' : dev.vif,
- 'evtchn' : dev.evtchn['port1'],
- 'tx_shmem_frame' : val['tx_shmem_frame'],
- 'rx_shmem_frame' : val['rx_shmem_frame'] })
- d = defer.Deferred()
- d.addCallback(self.factory.respond_be_connect)
- self.factory.writeRequest(msg, response=d)
-
- def send_interface_connected(self, vif, response=None):
- dev = self.devices[vif]
- msg = packMsg('netif_fe_interface_status_changed_t',
- { 'handle' : dev.vif,
- 'status' : NETIF_INTERFACE_STATUS_CONNECTED,
- 'evtchn' : dev.evtchn['port2'],
- 'mac' : dev.mac })
- self.writeRequest(msg, response=response)
-
- def send_be_create(self, vif, response=None):
- dev = self.devices[vif]
- msg = packMsg('netif_be_create_t',
- { 'domid' : self.dom,
- 'netif_handle' : dev.vif,
- 'mac' : dev.mac })
- self.factory.writeRequest(msg, response=response)
-
- def send_be_disconnect(self, vif, response=None):
- dev = self.devices[vif]
- msg = packMsg('netif_be_disconnect_t',
- { 'domid' : self.dom,
- 'netif_handle' : dev.vif })
- self.factory.writeRequest(msg, response=response)
-
- def send_be_destroy(self, vif, response=None):
- dev = self.devices[vif]
- del self.devices[vif]
- msg = packMsg('netif_be_destroy_t',
- { 'domid' : self.dom,
- 'netif_handle' : vif })
- self.factory.writeRequest(msg, response=response)
+ vif = val['handle']
+ print
+ print "recv_fe_interface_connect", val
+ dev = self.getDevice(vif)
+ if dev:
+ dev.recv_fe_interface_connect(val, req)
+ else:
+ log.error('Received netif_fe_interface_connect for unknown vif: dom=%d vif=%d',
+ self.dom, vif)
diff --git a/tools/python/xen/xend/server/params.py b/tools/python/xen/xend/server/params.py
index fa3cee9b14..bb5277aecd 100644
--- a/tools/python/xen/xend/server/params.py
+++ b/tools/python/xen/xend/server/params.py
@@ -1,8 +1,10 @@
# The following parameters could be placed in a configuration file.
-PID_FILE = '/var/run/xend.pid'
+XEND_PID_FILE = '/var/run/xend.pid'
+XFRD_PID_FILE = '/var/run/xfrd.pid'
+XEND_TRACE_FILE = '/var/log/xend.trace'
+
USER = 'root'
-CONTROL_DIR = '/var/run/xend'
-MGMT_SOCK = 'xendsock' # relative to CONTROL_DIR
+
EVENT_PORT = 8001
CONSOLE_PORT_BASE = 9600
diff --git a/tools/python/xen/xend/server/usbif.py b/tools/python/xen/xend/server/usbif.py
new file mode 100644
index 0000000000..d90997634b
--- /dev/null
+++ b/tools/python/xen/xend/server/usbif.py
@@ -0,0 +1,368 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+# Copyright (C) 2004 Intel Research Cambridge
+# Copyright (C) 2004 Mark Williamson <mark.williamson@cl.cam.ac.uk>
+"""Support for virtual USB hubs.
+"""
+
+from twisted.internet import defer
+#defer.Deferred.debug = 1
+
+from xen.xend import sxp
+from xen.xend.XendLogging import log
+from xen.xend.XendError import XendError
+
+import channel
+import controller
+from messages import *
+
+class UsbifBackendController(controller.BackendController):
+ """ Handler for the 'back-end' channel to a USB hub domain.
+ Must be connected using connect() before it can be used.
+ Do not create directly - use getBackend() on the UsbifController.
+ """
+
+ def __init__(self, ctrl, dom):
+ controller.BackendController.__init__(self, ctrl, dom)
+ self.connected = 0
+ self.evtchn = None
+ self.addMethod(CMSG_USBIF_BE,
+ CMSG_USBIF_BE_DRIVER_STATUS_CHANGED,
+ self.recv_be_driver_status_changed)
+ self.registerChannel()
+
+ def __str__(self):
+ return '<UsbifBackendController %d>' % (self.dom)
+
+ def recv_be_driver_status_changed(self, msg, req):
+ """Request handler for be_driver_status_changed messages.
+
+ @param msg: message
+ @type msg: xu message
+ @param req: request flag (true if the msg is a request)
+ @type req: bool
+ """
+ val = unpackMsg('usbif_be_driver_status_changed_t', msg)
+ status = val['status']
+
+class UsbifBackendInterface(controller.BackendInterface):
+ """Handler for the 'back-end' channel to a network device driver domain
+ on behalf of a front-end domain.
+
+ Each network device is handled separately, so we add no functionality
+ here.
+ """
+ def __init__(self, ctrl, dom):
+ controller.BackendInterface.__init__(self, ctrl, dom, 0)
+ self.connected = 0
+ self.connecting = False
+
+ def connect(self, recreate=0):
+ """Connect the controller to the usbif control interface.
+
+ @param recreate: true if after xend restart
+ @return: deferred
+ """
+ log.debug("Connecting usbif %s", str(self))
+ if recreate or self.connected or self.connecting:
+ d = defer.succeed(self)
+ else:
+ self.connecting = True
+ d = self.send_be_create()
+ d.addCallback(self.respond_be_create)
+ return d
+
+ def send_be_create(self):
+ d = defer.Deferred()
+ msg = packMsg('usbif_be_create_t',
+ { 'domid' : self.controller.dom })
+ self.writeRequest(msg, response=d)
+ return d
+
+ def respond_be_create(self, msg):
+ val = unpackMsg('usbif_be_create_t', msg)
+ log.debug('>UsbifBackendController>respond_be_create> %s', str(val))
+ self.connected = True
+ return self
+
+ def destroy(self):
+ """Disconnect from the usbif control interface and destroy it.
+ """
+ def cb_destroy(val):
+ self.send_be_destroy()
+ d = defer.Deferred()
+ d.addCallback(cb_destroy)
+ self.send_be_disconnect(response=d)
+
+ def send_be_disconnect(self, response=None):
+ log.debug('>UsbifBackendController>send_be_disconnect> %s', str(self))
+ msg = packMsg('usbif_be_disconnect_t',
+ { 'domid' : self.controller.dom })
+ self.writeRequest(msg, response=response)
+
+ def send_be_destroy(self, response=None):
+ log.debug('>UsbifBackendController>send_be_destroy> %s', str(self))
+ msg = packMsg('usbif_be_destroy_t',
+ { 'domid' : self.controller.dom })
+ self.writeRequest(msg, response=response)
+
+ def send_be_claim_port(self, path):
+ d=defer.Deferred()
+ log.debug(">UsbifBackendController>send_be_claim_port> about to claim port %s" % path)
+ def cb(blah): log.debug(">UsbifBackendController> Claim port completed")
+ d.addCallback(cb)
+ msg = packMsg('usbif_be_claim_port_t',
+ { 'domid' : self.controller.dom,
+ 'path' : path,
+ 'usbif_port' : self.controller.devices[path],
+ 'status' : 0})
+ self.writeRequest(msg, response=d)
+ # No need to add any callbacks, since the guest polls its virtual ports
+ # anyhow, somewhat like a UHCI controller ;-)
+ return d
+
+ def send_be_release_port(self, path):
+ d=defer.Deferred()
+ def cb(blah): log.debug(">UsbifBackendController> Release port completed")
+ d.addCallback(cb)
+ msg = packMsg('usbif_be_release_port_t',
+ { 'domid' : self.controller.dom,
+ 'path' : path })
+ self.writeRequest(msg, response)
+ # No need to add any callbacks, since the guest polls its virtual ports
+ # anyhow, somewhat like a UHCI controller ;-)
+
+ def connectInterface(self, val):
+ self.evtchn = channel.eventChannel(0, self.controller.dom)
+ log.debug(">UsbifBackendController>connectInterface> connecting usbif to event channel %s ports=%d:%d",
+ str(self), self.evtchn['port1'], self.evtchn['port2'])
+ msg = packMsg('usbif_be_connect_t',
+ { 'domid' : self.controller.dom,
+ 'evtchn' : self.evtchn['port1'],
+ 'shmem_frame' : val['shmem_frame'],
+ 'bandwidth' : 500 # XXX fix bandwidth!
+ })
+ d = defer.Deferred()
+ d.addCallback(self.respond_be_connect)
+ self.writeRequest(msg, response=d)
+
+ def respond_be_connect(self, msg):
+ """Response handler for a be_connect message.
+
+ @param msg: message
+ @type msg: xu message
+ """
+ val = unpackMsg('usbif_be_connect_t', msg)
+ log.debug('>UsbifBackendController>respond_be_connect> %s, %s', str(self), str(val))
+ d = defer.Deferred()
+ def cb(blah):
+ log.debug(">UsbifBackendController> Successfully connected USB interface for domain %d" % self.controller.dom)
+ self.controller.claim_ports()
+ d.addCallback(cb)
+ self.send_fe_interface_status_changed(d)
+
+ def send_fe_interface_status_changed(self, response=None):
+ msg = packMsg('usbif_fe_interface_status_changed_t',
+ { 'status' : USBIF_INTERFACE_STATUS_CONNECTED,
+ 'domid' : 0, ## FIXME: should be domid of backend
+ 'evtchn' : self.evtchn['port2'],
+ 'bandwidth' : 500,
+ 'num_ports' : len(self.controller.devices.keys())})
+ self.controller.writeRequest(msg, response=response)
+
+
+class UsbifControllerFactory(controller.SplitControllerFactory):
+ """Factory for creating USB interface controllers.
+ """
+
+ def __init__(self):
+ controller.ControllerFactory.__init__(self)
+ self.backendControllers = {}
+
+ def createController(self, dom, recreate=0):
+ """Create a USB device controller for a domain.
+
+ @param dom: domain
+ @type dom: int
+ @param recreate: if true it's a recreate (after xend restart)
+ @type recreate: bool
+ @return: block device controller
+ @rtype: UsbifController
+ """
+ usbif = self.getControllerByDom(dom)
+ if usbif is None:
+ usbif = UsbifController(self, dom)
+ self.addController(usbif)
+ return usbif
+
+ def getDomainDevices(self, dom):
+ """Get the block devices for a domain.
+
+ @param dom: domain
+ @type dom: int
+ @return: devices
+ @rtype: [device]
+ """
+ usbif = self.getControllerByDom(dom)
+ return (usbif and usbif.getDevices()) or []
+
+ def getDomainDevice(self, dom, vdev):
+ """Get a block device from a domain.
+
+ @param dom: domain
+ @type dom: int
+ @param vdev: device index
+ @type vdev: int
+ @return: device
+ @rtype: device
+ """
+ usbif = self.getControllerByDom(dom)
+ return (usbif and usbif.getDevice(vdev)) or None
+
+ def createBackendInterface(self, ctrl, dom, handle):
+ """Create a network device backend interface.
+
+ @param ctrl: controller
+ @param dom: backend domain
+ @param handle: interface handle
+ @return: backend interface
+ """
+ return UsbifBackendInterface(ctrl, dom)
+
+ def getBackendController(self, dom):
+ """Get the backend controller for a domain, creating
+ if necessary.
+
+ @param dom: backend domain
+ @return: backend controller
+ """
+ b = self.getBackendControllerByDomain(dom)
+ if b is None:
+ b = self.createBackendController(dom)
+ self.backendControllers[b.dom] = b
+ return b
+
+ def createBackendController(self, dom):
+ return UsbifBackendController(self, dom)
+
+class UsbifController(controller.SplitController):
+ """USB device interface controller. Handles all USB devices
+ for a domain.
+ """
+
+ def __init__(self, factory, dom):
+ """Create a USB device controller.
+ Do not call directly - use createController() on the factory instead.
+ """
+ controller.SplitController.__init__(self, factory, dom)
+ self.num_ports = 0
+ self.devices = {}
+ self.addMethod(CMSG_USBIF_FE,
+ CMSG_USBIF_FE_DRIVER_STATUS_CHANGED,
+ self.recv_fe_driver_status_changed)
+ self.addMethod(CMSG_USBIF_FE,
+ CMSG_USBIF_FE_INTERFACE_CONNECT,
+ self.recv_fe_interface_connect)
+ self.registerChannel()
+ try:
+ self.backendDomain = 0 #int(sxp.child_value(config, 'backend', '0')) TODO: configurable backends
+ except:
+ raise XendError('invalid backend domain')
+
+
+ def sxpr(self):
+ val = ['usbif', ['dom', self.dom]]
+ return val
+
+ def createBackend(self, dom, handle):
+ return UsbifBackendController(self, dom, handle)
+
+ def getDevices(self):
+ return self.devices.values()
+
+ def attachDevice(self, path, recreate=0):
+ """Add privileges for a particular device to the domain.
+ @param path: the Linux-style path to the device port
+ """
+ self.devices[path[1][1]] = self.num_ports
+ self.num_ports += 1
+ log.debug(">UsbifController>attachDevice> device: %s, port: %d" %
+ (str(path), self.num_ports ) )
+
+ backend =self.getBackendInterface(self.backendDomain)
+
+ def cb(blah):
+ log.debug(">UsbifController> Backend created")
+ pass
+ d = backend.connect()
+ d.addCallback(cb) # Chaining the claim port operation
+ return d
+
+
+ def removeDevice(self, path):
+ self.delDevice(path)
+ backend = self.getBackendInterface(self.backendDomain)
+ return backend.send_be_release_port(path)
+
+ def delDevice(self, path):
+ if path in self.devices:
+ del self.devices[path]
+
+ def attachPort(self, path, recreate=0):
+ """Attach a device to the specified interface.
+ On success the returned deferred will be called with the device.
+
+ @return: deferred
+ @rtype: Deferred
+ """
+ return self.attachDevice(path)
+
+ def destroy(self):
+ """Destroy the controller and all devices.
+ """
+ log.debug("Destroying usbif domain=%d", self.dom)
+ self.destroyBackends()
+
+ def destroyDevices(self):
+ """Destroy all devices.
+ """
+ for path in self.getDevices():
+ self.removeDevice(path)
+
+ def destroyBackends(self):
+ for backend in self.getBackendInterfaces():
+ backend.destroy()
+
+ def recv_fe_driver_status_changed(self, msg, req):
+ val = unpackMsg('usbif_fe_driver_status_changed_t', msg)
+ log.debug('>UsbifController>recv_fe_driver_status_changed> %s', str(val))
+ # For each backend?
+ msg = packMsg('usbif_fe_interface_status_changed_t',
+ { 'status' : USBIF_INTERFACE_STATUS_DISCONNECTED,
+ 'domid' : 0, ## FIXME: should be domid of backend
+ 'evtchn' : 0 })
+ d = defer.Deferred()
+ d.addCallback(self.disconnected_resp)
+ self.writeRequest(msg)
+
+ def disconnected_resp(self, msg):
+ val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
+ if val['status'] != USBIF_INTERFACE_STATUS_DISCONNECTED:
+ log.error(">UsbifController>disconnected_resp> unexpected status change")
+ else:
+ log.debug(">UsbifController>disconnected_resp> interface disconnected OK")
+
+ def recv_fe_interface_connect(self, msg, req):
+ val = unpackMsg('usbif_fe_interface_status_changed_t', msg)
+ log.debug(">UsbifController>recv_fe_interface_connect> notifying backend")
+ backend = self.getBackendInterfaceByHandle(0)
+ if backend:
+ d = backend.connectInterface(val)
+ else:
+ log.error('>UsbifController>recv_fe_interface_connect> unknown interface')
+
+ def claim_ports(self):
+ backend = self.getBackendInterfaceByHandle(0)
+ for path in self.devices.keys():
+ log.debug(">UsbifController>claim_ports> claiming port... %s" % path)
+ backend.send_be_claim_port(path)
+
diff --git a/tools/python/xen/xend/sxp.py b/tools/python/xen/xend/sxp.py
index 4fd9e9a92b..e2c0de5c5b 100644
--- a/tools/python/xen/xend/sxp.py
+++ b/tools/python/xen/xend/sxp.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""
Input-driven parsing for s-expression (sxp) format.
@@ -17,6 +17,7 @@ import types
import errno
import string
from StringIO import StringIO
+from xen.util.ip import _readline, _readlines
__all__ = [
"mime_type",
@@ -41,7 +42,8 @@ __all__ = [
"has_id",
"with_id",
"child_with_id",
- "elements",
+ "elements",
+ "merge",
"to_string",
"from_string",
"all_from_string",
@@ -595,6 +597,77 @@ def elements(sxpr, ctxt=None):
yield v
i += 1
+def merge(s1, s2):
+ """Merge sxprs s1 and s2.
+ Returns an sxpr containing all the fields from s1 and s2, with
+ entries in s1 overriding s2. Recursively merges fields.
+
+ @param s1 sxpr
+ @param s2 sxpr
+ @return merged sxpr
+ """
+ if s1 is None:
+ val = s2
+ elif s2 is None:
+ val = s1
+ elif elementp(s1):
+ name1 = name(s1)
+ (m1, v1) = child_map(s1)
+ (m2, v2) = child_map(s2)
+ val = [name1]
+ for (k1, f1) in m1.items():
+ merge_list(val, f1, m2.get(k1, []))
+ for (k2, f2) in m2.items():
+ if k2 in m1: continue
+ val.extend(f2)
+ val.extend(v1)
+ else:
+ val = s1
+ return val
+
+def merge_list(sxpr, l1, l2):
+ """Merge element lists l1 and l2 into sxpr.
+ The lists l1 and l2 are all element with the same name.
+ Values from l1 are merged with values in l2 and stored in sxpr.
+ If one list is longer than the other the excess values are used
+ as they are.
+
+ @param sxpr to merge into
+ @param l1 sxpr list
+ @param l2 sxpr list
+ @return modified sxpr
+ """
+ n1 = len(l1)
+ n2 = len(l2)
+ nmin = min(n1, n2)
+ for i in range(0, nmin):
+ sxpr.append(merge(l1[i], l2[i]))
+ for i in range(nmin, n1):
+ sxpr.append(l1[i])
+ for i in range(nmin, n2):
+ sxpr.append(l2[i])
+ return sxpr
+
+def child_map(sxpr):
+ """Get a dict of the elements in sxpr and a list of its values.
+ The dict maps element name to the list of elements with that name,
+ and the list is the non-element children.
+
+ @param sxpr
+ @return (dict, list)
+ """
+ m = {}
+ v = []
+ for x in children(sxpr):
+ if elementp(x):
+ n = name(x)
+ l = m.get(n, [])
+ l.append(x)
+ m[n] = l
+ else:
+ v.append(x)
+ return (m, v)
+
def to_string(sxpr):
"""Convert an sxpr to a string.
@@ -641,7 +714,7 @@ def parse(io):
"""
pin = Parser()
while 1:
- buf = io.readline()
+ buf = _readline(io)
pin.input(buf)
if len(buf) == 0:
break
diff --git a/tools/python/xen/xend/util.py b/tools/python/xen/xend/util.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tools/python/xen/xend/util.py
diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py
index 286e1d3478..e79e02383c 100644
--- a/tools/python/xen/xm/create.py
+++ b/tools/python/xen/xm/create.py
@@ -20,48 +20,49 @@ Create a domain.
Domain creation parameters can be set by command-line switches, from
a python configuration script or an SXP config file. See documentation
-for --defaults, --config. Configuration variables can be set using
+for --defconfig, --config. Configuration variables can be set using
VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
""")
gopts.opt('help', short='h',
- fn=set_true, default=0,
- use="Print this help.")
+ fn=set_true, default=0,
+ use="Print this help.")
gopts.opt('help_config',
fn=set_true, default=0,
- use="Print help for configuration file.")
+ use="Print help for the configuration script.")
gopts.opt('quiet', short='q',
- fn=set_true, default=0,
- use="Quiet.")
+ fn=set_true, default=0,
+ use="Quiet.")
gopts.opt('path', val='PATH',
- fn=set_value, default='.:/etc/xen',
- use="Search path for default scripts.")
-
-gopts.opt('defaults', short='f', val='FILE',
- fn=set_value, default='xmdefaults',
- use="""Use the given Python defaults script.
-The defaults script is loaded after arguments have been processed.
-Each command-line option sets a configuration variable named after
-its long option name, and these variables are placed in the
-environment of the script before it is loaded.
-Variables for options that may be repeated have list values.
-Other variables can be set using VAR=VAL on the command line.
-
-After the script is loaded, option values that were not set on the
-command line are replaced by the values set in the script.
-""")
+ fn=set_value, default='.:/etc/xen',
+ use="""Search path for configuration scripts.
+ The value of PATH is a colon-separated directory list.""")
+
+gopts.opt('defconfig', short='f', val='FILE',
+ fn=set_value, default='xmdefconfig',
+ use="""Use the given Python configuration script.
+ The configuration script is loaded after arguments have been processed.
+ Each command-line option sets a configuration variable named after
+ its long option name, and these variables are placed in the
+ environment of the script before it is loaded.
+ Variables for options that may be repeated have list values.
+ Other variables can be set using VAR=VAL on the command line.
+
+ After the script is loaded, option values that were not set on the
+ command line are replaced by the values set in the script.""")
+
+gopts.default('defconfig')
gopts.opt('config', short='F', val='FILE',
- fn=set_value, default=None,
- use="""Domain configuration to use (SXP).
-SXP is the underlying configuration format used by Xen.
-SXP configs can be hand-written or generated from Python defaults
-scripts, using the -n (dryrun) option to print the config.
-""")
+ fn=set_value, default=None,
+ use="""Domain configuration to use (SXP).
+ SXP is the underlying configuration format used by Xen.
+ SXP configurations can be hand-written or generated from Python configuration
+ scripts, using the -n (dryrun) option to print the configuration.""")
gopts.opt('load', short='L', val='FILE',
fn=set_value, default=None,
@@ -69,17 +70,20 @@ gopts.opt('load', short='L', val='FILE',
gopts.opt('dryrun', short='n',
fn=set_true, default=0,
- use="""Dry run - print the config but don't create the domain.
-The defaults file is loaded and the SXP configuration is created and printed.
-""")
+ use="""Dry run - print the configuration but don't create the domain.
+ Loads the configuration script, creates the SXP configuration and prints it.""")
+
+gopts.opt('paused', short='p',
+ fn=set_true, default=0,
+ use='Leave the domain paused after it is created.')
gopts.opt('console_autoconnect', short='c',
fn=set_true, default=0,
- use="Connect to console after domain is created.")
+ use="Connect to the console after the domain is created.")
gopts.var('name', val='NAME',
fn=set_value, default=None,
- use="Domain name.")
+ use="Domain name. Must be unique.")
gopts.var('kernel', val='FILE',
fn=set_value, default=None,
@@ -94,9 +98,26 @@ gopts.var('builder', val='FUNCTION',
use="Function to use to build the domain.")
gopts.var('memory', val='MEMORY',
- fn=set_value, default=128,
+ fn=set_int, default=128,
use="Domain memory in MB.")
+gopts.var('maxmem', val='MEMORY',
+ fn=set_int, default=None,
+ use="Maximum domain memory in MB.")
+
+gopts.var('cpu', val='CPU',
+ fn=set_int, default=None,
+ use="CPU to run the domain on.")
+
+gopts.var('vcpus', val='VCPUS',
+ fn=set_int, default=1,
+ use="# of Virtual CPUS in domain.")
+
+gopts.var('cpu_weight', val='WEIGHT',
+ fn=set_float, default=None,
+ use="""Set the new domain's cpu weight.
+ WEIGHT is a float that controls the domain's share of the cpu.""")
+
gopts.var('console', val='PORT',
fn=set_int, default=None,
use="Console port to use. Default is 9600 + domain id.")
@@ -104,10 +125,9 @@ gopts.var('console', val='PORT',
gopts.var('restart', val='onreboot|always|never',
fn=set_value, default=None,
use="""Whether the domain should be restarted on exit.
- - onreboot: restart on exit with shutdown code reboot
- - always: always restart on exit, ignore exit code
- - never: never restart on exit, ignore exit code
- """)
+ - onreboot: restart on exit with shutdown code reboot
+ - always: always restart on exit, ignore exit code
+ - never: never restart on exit, ignore exit code""")
gopts.var('blkif', val='no|yes',
fn=set_bool, default=0,
@@ -117,48 +137,53 @@ gopts.var('netif', val='no|yes',
fn=set_bool, default=0,
use="Make the domain a network interface backend.")
-gopts.var('disk', val='phy:DEV,VDEV,MODE',
+gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
fn=append_value, default=[],
use="""Add a disk device to a domain. The physical device is DEV,
- which is exported to the domain as VDEV. The disk is read-only if MODE
- is 'r', read-write if MODE is 'w'.
- The option may be repeated to add more than one disk.
- """)
+ which is exported to the domain as VDEV. The disk is read-only if MODE
+ is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
+ backend driver domain to use for the disk.
+ The option may be repeated to add more than one disk.""")
gopts.var('pci', val='BUS,DEV,FUNC',
fn=append_value, default=[],
use="""Add a PCI device to a domain, using given params (in hex).
For example '-pci c0,02,1a'.
- The option may be repeated to add more than one pci device.
- """)
+ The option may be repeated to add more than one pci device.""")
+
+gopts.var('usb', val='PATH',
+ fn=append_value, default=[],
+ use="""Add a physical USB port to a domain, as specified by the path
+ to that port. This option may be repeated to add more than one port.""")
gopts.var('ipaddr', val="IPADDR",
fn=append_value, default=[],
use="Add an IP address to the domain.")
-gopts.var('vif', val="mac=MAC,bridge=BRIDGE,script=SCRIPT",
+gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM",
fn=append_value, default=[],
use="""Add a network interface with the given MAC address and bridge.
- The vif is configured by calling the given configuration script.
- If mac is not specified a random MAC address is used.
- If bridge is not specified the default bridge is used.
- If script is not specified the default script is used.
- This option may be repeated to add more than one vif.
- Specifying vifs will increase the number of interfaces as needed.
- """)
+ The vif is configured by calling the given configuration script.
+ If mac is not specified a random MAC address is used.
+ The MAC address of the backend interface can be selected with be_mac.
+ If not specified then the network backend chooses it's own MAC address.
+ If bridge is not specified the default bridge is used.
+ If script is not specified the default script is used.
+ If backend is not specified the default backend driver domain is used.
+ This option may be repeated to add more than one vif.
+ Specifying vifs will increase the number of interfaces as needed.""")
gopts.var('nics', val="NUM",
fn=set_int, default=1,
use="""Set the number of network interfaces.
- Use the vif option to define interface parameters, otherwise
- defaults are used. Specifying vifs will increase the
- number of interfaces as needed.
- """)
+ Use the vif option to define interface parameters, otherwise
+ defaults are used. Specifying vifs will increase the
+ number of interfaces as needed.""")
gopts.var('root', val='DEVICE',
fn=set_value, default='',
use="""Set the root= parameter on the kernel command line.
- Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
+ Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
gopts.var('extra', val="ARGS",
fn=set_value, default='',
@@ -196,6 +221,18 @@ gopts.var('nfs_root', val="PATH",
fn=set_value, default=None,
use="Set the path of the root NFS directory.")
+gopts.var('memmap', val='FILE',
+ fn=set_value, default='',
+ use="Path to memap SXP file.")
+
+gopts.var('device_model', val='FILE',
+ fn=set_value, default='',
+ use="Path to device model program.")
+
+gopts.var('device_config', val='FILE',
+ fn=set_value, default='',
+ use="Path to device model configuration.")
+
def strip(pre, s):
"""Strip prefix 'pre' if present.
"""
@@ -219,16 +256,21 @@ def configure_image(config, vals):
config_image.append(['root', cmdline_root])
if vals.extra:
config_image.append(['args', vals.extra])
+ if vals.vcpus:
+ config_image.append(['vcpus', vals.vcpus])
config.append(['image', config_image ])
+
def configure_disks(config_devs, vals):
"""Create the config for disks (virtual block devices).
"""
- for (uname, dev, mode) in vals.disk:
+ for (uname, dev, mode, backend) in vals.disk:
config_vbd = ['vbd',
['uname', uname],
['dev', dev ],
['mode', mode ] ]
+ if backend:
+ config_vbd.append(['backend', backend])
config_devs.append(['device', config_vbd])
def configure_pci(config_devs, vals):
@@ -238,6 +280,11 @@ def configure_pci(config_devs, vals):
config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
config_devs.append(['device', config_pci])
+def configure_usb(config_devs, vals):
+ for path in vals.usb:
+ config_usb = ['usb', ['path', path]]
+ config_devs.append(['device', config_usb])
+
def randomMAC():
"""Generate a random MAC address.
@@ -248,8 +295,9 @@ def randomMAC():
The remaining 3 fields are random, with the first bit of the first
random field set 0.
- returns MAC address string
+ @return: MAC address string
"""
+ random.seed()
mac = [ 0xaa, 0x00, 0x00,
random.randint(0x00, 0x7f),
random.randint(0x00, 0xff),
@@ -266,18 +314,32 @@ def configure_vifs(config_devs, vals):
if idx < len(vifs):
d = vifs[idx]
mac = d.get('mac')
+ if not mac:
+ mac = randomMAC()
+ be_mac = d.get('be_mac')
bridge = d.get('bridge')
script = d.get('script')
+ backend = d.get('backend')
+ ip = d.get('ip')
else:
mac = randomMAC()
+ be_mac = None
bridge = None
script = None
+ backend = None
+ ip = None
config_vif = ['vif']
config_vif.append(['mac', mac])
+ if be_mac:
+ config_vif.append(['be_mac', be_mac])
if bridge:
config_vif.append(['bridge', bridge])
if script:
config_vif.append(['script', script])
+ if backend:
+ config_vif.append(['backend', backend])
+ if ip:
+ config_vif.append(['ip', ip])
config_devs.append(['device', config_vif])
def configure_vfr(config, vals):
@@ -288,6 +350,15 @@ def configure_vfr(config, vals):
config_vfr.append(['vif', ['id', idx], ['ip', ip]])
config.append(config_vfr)
+def configure_vmx(config_devs, vals):
+ """Create the config for VMX devices.
+ """
+ memmap = vals.memmap
+ device_model = vals.device_model
+ device_config = vals.device_config
+ config_devs.append(['memmap', memmap])
+ config_devs.append(['device_model', device_model])
+ config_devs.append(['device_config', device_config])
def make_config(vals):
"""Create the domain configuration.
@@ -295,9 +366,13 @@ def make_config(vals):
config = ['vm',
['name', vals.name ],
- ['memory', vals.memory ] ]
- if vals.cpu:
+ ['memory', vals.memory ]]
+ if vals.maxmem:
+ config.append(['maxmem', vals.maxmem])
+ if vals.cpu is not None:
config.append(['cpu', vals.cpu])
+ if vals.cpu_weight is not None:
+ config.append(['cpu_weight', vals.cpu_weight])
if vals.blkif:
config.append(['backend', ['blkif']])
if vals.netif:
@@ -312,6 +387,8 @@ def make_config(vals):
configure_disks(config_devs, vals)
configure_pci(config_devs, vals)
configure_vifs(config_devs, vals)
+ configure_usb(config_devs, vals)
+ configure_vmx(config_devs, vals)
config += config_devs
return config
@@ -320,7 +397,12 @@ def preprocess_disk(opts, vals):
disk = []
for v in vals.disk:
d = v.split(',')
- if len(d) != 3:
+ n = len(d)
+ if n == 3:
+ d.append(None)
+ elif n == 4:
+ pass
+ else:
opts.err('Invalid disk specifier: ' + v)
disk.append(d)
vals.disk = disk
@@ -347,7 +429,7 @@ def preprocess_vifs(opts, vals):
(k, v) = b.strip().split('=', 1)
k = k.strip()
v = v.strip()
- if k not in ['mac', 'bridge']:
+ if k not in ['mac', 'be_mac', 'bridge', 'script', 'backend', 'ip']:
opts.err('Invalid vif specifier: ' + vif)
d[k] = v
vifs.append(d)
@@ -407,10 +489,11 @@ def make_domain(opts, config):
console_port = int(sxp.child_value(console_info, 'console_port'))
else:
console_port = None
-
- if server.xend_domain_unpause(dom) < 0:
- server.xend_domain_destroy(dom)
- opts.err("Failed to unpause domain %s" % dom)
+
+ if not opts.vals.paused:
+ if server.xend_domain_unpause(dom) < 0:
+ server.xend_domain_destroy(dom)
+ opts.err("Failed to unpause domain %s" % dom)
opts.info("Started domain %s, console on port %d"
% (dom, console_port))
return (dom, console_port)
@@ -421,7 +504,7 @@ def main(argv):
if opts.vals.help:
opts.usage()
if opts.vals.help or opts.vals.help_config:
- opts.load_defaults(help=1)
+ opts.load_defconfig(help=1)
if opts.vals.help or opts.vals.help_config:
return
# Process remaining args as config variables.
@@ -432,8 +515,10 @@ def main(argv):
if opts.vals.config:
config = opts.vals.config
else:
- opts.load_defaults()
+ opts.load_defconfig()
preprocess(opts, opts.vals)
+ if not opts.getopt('name') and opts.getopt('defconfig'):
+ opts.setopt('name', os.path.basename(opts.getopt('defconfig')))
config = make_config(opts.vals)
if opts.vals.dryrun:
PrettyPrint.prettyprint(config)
diff --git a/tools/python/xen/xm/help.py b/tools/python/xen/xm/help.py
index 88df4412c0..8efaf2946a 100644
--- a/tools/python/xen/xm/help.py
+++ b/tools/python/xen/xm/help.py
@@ -1,6 +1,6 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
-"""Variable definition and help support for Python defaults files.
+"""Variable definition and help support for Python defconfig files.
"""
import sys
@@ -12,7 +12,7 @@ class Vars:
def __init__(self, name, help, env):
"""Create a variable set.
- name name of the defaults file
+ name name of the defconfig file
help help flag
env local environment
"""
diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py
index 5fa8625413..411da44106 100644
--- a/tools/python/xen/xm/main.py
+++ b/tools/python/xen/xm/main.py
@@ -11,7 +11,8 @@ from xen.xend import PrettyPrint
from xen.xend import sxp
from xen.xend.XendClient import XendError, server
from xen.xend.XendClient import main as xend_client_main
-from xen.xm import create, destroy, shutdown
+from xen.xm import create, destroy, migrate, shutdown, sysrq
+from xen.xm.opts import *
class Group:
@@ -282,21 +283,33 @@ class ProgRestore(Prog):
info = """Create a domain from a saved state."""
def help(self, args):
- print args[0], "FILE [CONFIG]"
- print "\nRestore a domain from FILE using configuration CONFIG."
+ print args[0], "FILE"
+ print "\nRestore a domain from FILE."
- def main(self, help, args):
+ def main(self, args):
if len(args) < 2: self.err("%s: Missing arguments" % args[0])
- savefile = os.path.abspath(args[1])
- if len(args) >= 3:
- configfile = os.path.abspath(args[2])
- else:
- configfile = None
- info = server.xend_domain_restore(savefile, configfile)
+ savefile = os.path.abspath(args[1])
+ info = server.xend_domain_restore(savefile)
PrettyPrint.prettyprint(info)
+ id = sxp.child_value(info, 'id')
+ if id is not None:
+ server.xend_domain_unpause(id)
xm.prog(ProgRestore)
+class ProgMigrate(Prog):
+ group = 'domain'
+ name = "migrate"
+ info = """Migrate a domain to another machine."""
+
+ def help(self, args):
+ migrate.help([self.name] + args)
+
+ def main(self, args):
+ migrate.main(args)
+
+xm.prog(ProgMigrate)
+
class ProgList(Prog):
group = 'domain'
name = "list"
@@ -388,6 +401,19 @@ class ProgShutdown(Prog):
xm.prog(ProgShutdown)
+class ProgSysrq(Prog):
+ group = 'domain'
+ name = "sysrq"
+ info = """Send a sysrq to a domain."""
+
+ def help(self, args):
+ sysrq.main([args[0], '-h'])
+
+ def main(self, args):
+ sysrq.main(args)
+
+xm.prog(ProgSysrq)
+
class ProgPause(Prog):
group = 'domain'
name = "pause"
@@ -431,8 +457,9 @@ class ProgPincpu(Prog):
def main(self, args):
if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0])
- v = map(int, args[1:3])
- server.xend_domain_pincpu(*v)
+ dom = args[1]
+ cpu = int(args[2])
+ server.xend_domain_pincpu(dom, cpu)
xm.prog(ProgPincpu)
@@ -447,11 +474,30 @@ class ProgMaxmem(Prog):
def main(self, args):
if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0])
- v = map(int, args[1:3])
- server.xend_domain_maxmem_set(*v)
+ dom = args[1]
+ mem = int(args[2])
+ server.xend_domain_maxmem_set(dom, mem)
xm.prog(ProgMaxmem)
+class ProgBalloon(Prog):
+ group = 'domain'
+ name = 'balloon'
+ info = """Set the domain's memory footprint using the balloon driver."""
+
+ def help(self, args):
+ print args[0], "DOM MEMORY_TARGET"
+ print """\nRequest domain DOM to adjust its memory footprint to
+MEMORY_TARGET megabytes"""
+
+ def main(self, args):
+ if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0])
+ dom = args[1]
+ mem_target = int(args[2])
+ server.xend_domain_mem_target_set(dom, mem_target)
+
+xm.prog(ProgBalloon)
+
class ProgDomid(Prog):
group = 'domain'
name = 'domid'
@@ -497,8 +543,9 @@ class ProgBvt(Prog):
def main(self, args):
if len(args) != 7: self.err("%s: Invalid argument(s)" % args[0])
- v = map(long, args[1:7])
- server.xend_domain_cpu_bvt_set(*v)
+ dom = args[1]
+ v = map(long, args[2:7])
+ server.xend_domain_cpu_bvt_set(dom, *v)
xm.prog(ProgBvt)
@@ -519,39 +566,6 @@ class ProgBvtslice(Prog):
xm.prog(ProgBvtslice)
-class ProgFbvt(Prog):
- group = 'scheduler'
- name = "fbvt"
- info = """Set FBVT scheduler parameters."""
-
- def help(self, args):
- print args[0], "DOM MCUADV WARP WARPL WARPU"
- print '\nSet Fair Borrowed Virtual Time scheduler parameters.'
-
- def main(self, args):
- if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0])
- v = map(int, args[1:6])
- server.xend_domain_cpu_fbvt_set(*v)
-
-xm.prog(ProgFbvt)
-
-class ProgFbvtslice(Prog):
- group = 'scheduler'
- name = "fbvt_ctxallow"
- info = """Set the FBVT scheduler context switch allowance."""
-
- def help(self, args):
- print args[0], 'CTX_ALLOW'
- print '\nSet Fair Borrowed Virtual Time scheduler context switch allowance.'
-
- def main(self, args):
- if len(args) < 2: self.err('%s: Missing context switch allowance.'
- % args[0])
- ctx_allow = int(args[1])
- server.xend_node_cpu_fbvt_slice_set(ctx_allow)
-
-xm.prog(ProgFbvtslice)
-
class ProgAtropos(Prog):
group = 'scheduler'
@@ -563,9 +577,10 @@ class ProgAtropos(Prog):
print "\nSet atropos parameters."
def main(self, args):
- if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0])
- v = map(int, args[1:5])
- server.xend_domain_cpu_atropos_set(*v)
+ if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0])
+ dom = args[1]
+ v = map(int, args[2:6])
+ server.xend_domain_cpu_atropos_set(dom, *v)
xm.prog(ProgAtropos)
@@ -661,10 +676,34 @@ xm.prog(ProgCall)
class ProgDmesg(Prog):
group = 'host'
name = "dmesg"
- info = """Print Xen boot output."""
+ info = """Read or clear Xen's message buffer."""
+
+ gopts = Opts(use="""[-c|--clear]
+
+Read Xen's message buffer (boot output, warning and error messages) or clear
+its contents if the [-c|--clear] flag is specified.
+""")
+
+ gopts.opt('clear', short='c',
+ fn=set_true, default=0,
+ use="Clear the contents of the Xen message buffer.")
+
+ short_options = ['-c']
+ long_options = ['--clear']
+
+ def help(self, args):
+ self.gopts.argv = args
+ self.gopts.usage()
def main(self, args):
- print server.xend_node_dmesg()
+ self.gopts.parse(args)
+ if not (1 <= len(args) <=2):
+ self.gopts.err('Invalid arguments: ' + str(args))
+
+ if not self.gopts.vals.clear:
+ print server.xend_node_get_dmesg()
+ else:
+ server.xend_node_clear_dmesg()
xm.prog(ProgDmesg)
@@ -720,22 +759,26 @@ class ProgVbdCreate(Prog):
info = """Create a new virtual block device for a domain"""
def help(self, args):
- print args[0], "DOM UNAME DEV MODE"
+ print args[0], "DOM UNAME DEV MODE [BACKEND]"
print """
Create a virtual block device for a domain.
- UNAME - device to export, e.g. phys:hda2
- DEV - device name in the domain, e.g. xda1
- MODE - access mode: r for read, w for read-write
+ UNAME - device to export, e.g. phy:hda2
+ DEV - device name in the domain, e.g. sda1
+ MODE - access mode: r for read, w for read-write
+ BACKEND - backend driver domain
"""
def main(self, args):
- if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0])
+ n = len(args)
+ if n < 5 or n > 6: self.err("%s: Invalid argument(s)" % args[0])
dom = args[1]
vbd = ['vbd',
['uname', args[2]],
['dev', args[3]],
['mode', args[4]]]
+ if n == 6:
+ vbd.append(['backend', args[5]])
server.xend_domain_device_create(dom, vbd)
xm.prog(ProgVbdCreate)
@@ -749,13 +792,15 @@ class ProgVbdDestroy(Prog):
print args[0], "DOM DEV"
print """
Destroy vbd DEV attached to domain DOM. Detaches the device
-from the domain, but does not destroy the device contents."""
+from the domain, but does not destroy the device contents.
+The device indentifier DEV is the idx field in the device
+information. This is visible in 'xm vbd-list'."""
def main(self, args):
- if len(args!=3): self.err("%s: Invalid argument(s)" % args[0])
+ if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0])
dom = args[1]
dev = args[2]
- sever.xend_domain_device_destroy(dom, "vbd", dev)
+ server.xend_domain_device_destroy(dom, "vbd", dev)
xm.prog(ProgVbdDestroy)
diff --git a/tools/python/xen/xm/migrate.py b/tools/python/xen/xm/migrate.py
new file mode 100644
index 0000000000..147c0c4d08
--- /dev/null
+++ b/tools/python/xen/xm/migrate.py
@@ -0,0 +1,50 @@
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+
+"""Domain migration.
+"""
+
+import sys
+
+from xen.xend.XendClient import server
+from xen.xm.opts import *
+
+DOM0_NAME = 'Domain-0'
+DOM0_ID = '0'
+
+gopts = Opts(use="""[options] DOM HOST
+
+Migrate domain DOM to host HOST.
+The transfer daemon xfrd must be running on the
+local host and on HOST.
+""")
+
+gopts.opt('help', short='h',
+ fn=set_true, default=0,
+ use="Print this help.")
+
+gopts.opt('live', short='l',
+ fn=set_true, default=0,
+ use="Use live migration.")
+
+gopts.opt('resource', short='r', val='MBIT',
+ fn=set_int, default=0,
+ use="Set level of resource usage for migration.")
+
+def help(argv):
+ gopts.argv = argv
+ gopts.usage()
+
+def main(argv):
+ opts = gopts
+ args = opts.parse(argv)
+ if opts.vals.help:
+ opts.usage()
+ return
+ if len(args) != 2:
+ opts.err('Invalid arguments: ' + str(args))
+ dom = args[0]
+ dst = args[1]
+ if dom in [DOM0_NAME, DOM0_ID]:
+ opts.err('Cannot migrate ' + dom)
+ server.xend_domain_migrate(dom, dst, opts.vals.live, opts.vals.resource)
+
diff --git a/tools/python/xen/xm/opts.py b/tools/python/xen/xm/opts.py
index 9f34b6773c..f92c82dfe6 100644
--- a/tools/python/xen/xm/opts.py
+++ b/tools/python/xen/xm/opts.py
@@ -88,19 +88,31 @@ class Opt:
else:
return None
- def show(self):
- sep = ''
+ def format(self, str, start=' ', out=sys.stdout):
+ """Print a string, with consistent indentation at the start of lines.
+ """
+ lines = str.split('\n')
+ for l in lines:
+ l = l.strip()
+ if start:
+ out.write(start)
+ out.write(l)
+ out.write('\n')
+
+ def show(self, out=sys.stdout):
+ sep = ' '
for x in self.optkeys:
- print sep, x,
- sep = ','
+ out.write(sep)
+ out.write(x)
+ sep = ', '
if self.val:
- print self.val,
- print
+ out.write(' ')
+ out.write(self.val)
+ out.write('\n')
if self.use:
- print '\t',
- print self.use
+ self.format(self.use, out=out);
if self.val:
- print '\tDefault', self.default or 'None'
+ self.format('Default ' + str(self.default or 'None'), out=out)
def specify(self, k, v):
"""Specify the option. Called when the option is set
@@ -153,14 +165,12 @@ class OptVar(Opt):
def long_opt(self):
return None
- def show(self):
- print '%s=%s' %(self.optkeys[0], self.val)
- print
+ def show(self, out=sys.stdout):
+ print >>out, ' %s=%s' % (self.optkeys[0], self.val)
if self.use:
- print '\t',
- print self.use
+ self.format(self.use, out=out);
if self.val:
- print '\tDefault', self.default or 'None'
+ self.format('Default ' + str(self.default or 'None'), out=out)
class OptVals:
"""Class to hold option values.
@@ -194,6 +204,8 @@ class Opts:
self.vals.quiet = 0
# Variables for default scripts.
self.vars = {}
+ # Option to use for bare words.
+ self.default_opt = None
def __repr__(self):
return '\n'.join(map(str, self.options))
@@ -211,6 +223,15 @@ class Opts:
self.options_map[name] = x
return x
+ def default(self, name):
+ self.default_opt = name
+
+ def getdefault(self, val):
+ if self.default_opt is None:
+ return 0
+ opt = self.option(self.default_opt)
+ return opt.set(val)
+
def var(self, name, **args):
x = OptVar(self, name, **args)
self.options.append(x)
@@ -274,27 +295,30 @@ class Opts:
"""
self.argv = argv
- try:
- (vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts())
- except GetoptError, err:
- self.err(str(err))
-
- # hack to work around lack of gnu getopts parsing in python 2.2
- xargs = args
- while xargs[1:]:
- (v,xargs) = getopt(xargs[1:], self.short_opts(), self.long_opts())
- vals = vals + v
-
- # back to the real work
- self.args = args
- for (k, v) in vals:
- for opt in self.options:
- if opt.specify(k, v): break
- else:
- print >>sys.stderr, "Error: Unknown option:", k
- self.usage()
+ # hack to work around lack of gnu getopts parsing in python 2.2
+ args = argv[1:]
xargs = []
- for arg in args:
+ while args:
+ # let getopt parse whatever it feels like -- if anything
+ try:
+ (xvals, args) = getopt(args[0:],
+ self.short_opts(), self.long_opts())
+ except GetoptError, err:
+ self.err(str(err))
+
+ for (k, v) in xvals:
+ for opt in self.options:
+ if opt.specify(k, v): break
+ else:
+ print >>sys.stderr, "Error: Unknown option:", k
+ self.usage()
+
+ if not args:
+ break
+
+ # then process the 1st arg
+ (arg,args) = (args[0], args[1:])
+
isvar = 0
if '=' in arg:
(k, v) = arg.split('=', 1)
@@ -302,8 +326,11 @@ class Opts:
if opt.specify(k, v):
isvar = 1
break
+ elif self.getdefault(arg):
+ isvar = 1
if not isvar:
xargs.append(arg)
+
return xargs
def short_opts(self):
@@ -328,29 +355,48 @@ class Opts:
def usage(self):
print 'Usage: ', self.argv[0], self.use or 'OPTIONS'
+ print
for opt in self.options:
- print
opt.show()
+ print
+ if self.options:
+ print
+
+ def var_usage(self):
+ if self.vars:
+ print 'The config file defines the following variables:'
+ for var in self.vars:
+ var.show()
+ print
+ print
+
+ def config_usage(self):
+ if self.imports:
+ print 'The following are automically imported:'
+ for x in self.imports:
+ print ' ', x
+ print
+ self.var_usage()
- def load_defaults(self, help=0):
- """Load a defaults script. Assumes these options set:
+ def load_defconfig(self, help=0):
+ """Load a defconfig script. Assumes these options set:
'path' search path
- 'default' script name
+ 'defconfig' script name
"""
for x in [ '' ] + self.vals.path.split(':'):
if x:
- p = os.path.join(x, self.vals.defaults)
+ p = os.path.join(x, self.vals.defconfig)
else:
- p = self.vals.defaults
+ p = self.vals.defconfig
if os.path.exists(p):
- self.info('Using config file %s' % p)
+ self.info('Using config file "%s".' % p)
self.load(p, help)
break
else:
- self.err("Cannot open defaults file %s" % self.vals.defaults)
+ self.err('Cannot open config file "%s"' % self.vals.defconfig)
- def load(self, defaults, help):
- """Load a defaults file. Local variables in the file
+ def load(self, defconfig, help):
+ """Load a defconfig file. Local variables in the file
are used to set options with the same names.
Variables are not used to set options that are already specified.
"""
@@ -363,19 +409,17 @@ class Opts:
locals.update(self.vars)
cmd = '\n'.join(self.imports +
[ "from xen.xm.help import Vars",
- "xm_file = '%s'" % defaults,
+ "xm_file = '%s'" % defconfig,
"xm_help = %d" % help,
"xm_vars = Vars(xm_file, xm_help, locals())"
])
exec cmd in globals, locals
try:
- execfile(defaults, globals, locals)
+ execfile(defconfig, globals, locals)
except:
if not help: raise
if help:
- print 'The following imports are done automatically:'
- for x in self.imports:
- print x
+ self.config_usage()
return
# Extract the values set by the script and set the corresponding
# options, if not set on the command line.
@@ -409,7 +453,7 @@ def set_bool(opt, k, v):
def set_value(opt, k, v):
- """Set an option to a valoue."""
+ """Set an option to a value."""
opt.set(v)
def set_int(opt, k, v):
@@ -420,6 +464,14 @@ def set_int(opt, k, v):
opt.opts.err('Invalid value: ' + str(v))
opt.set(v)
+def set_float(opt, k, v):
+ """Set an option to a float value."""
+ try:
+ v = float(v)
+ except:
+ opt.opts.err('Invalid value: ' + str(v))
+ opt.set(v)
+
def append_value(opt, k, v):
"""Append a value to a list option."""
opt.append(v)
diff --git a/tools/python/xen/xm/shutdown.py b/tools/python/xen/xm/shutdown.py
index 73324ddd40..39ec78357d 100644
--- a/tools/python/xen/xm/shutdown.py
+++ b/tools/python/xen/xm/shutdown.py
@@ -46,7 +46,7 @@ def shutdown(opts, doms, mode, wait):
server.xend_domain_shutdown(d, mode)
if wait:
while doms:
- alive = domains()
+ alive = server.xend_domains()
dead = []
for d in doms:
if d in alive: continue
@@ -73,7 +73,8 @@ def shutdown_mode(opts):
return mode
def main_all(opts, args):
- shutdown(opts, None, opts.vals.wait)
+ mode = shutdown_mode(opts)
+ shutdown(opts, None, mode, opts.vals.wait)
def main_dom(opts, args):
if len(args) < 1: opts.err('Missing domain')
diff --git a/tools/python/xen/xm/sysrq.py b/tools/python/xen/xm/sysrq.py
new file mode 100644
index 0000000000..44827af094
--- /dev/null
+++ b/tools/python/xen/xm/sysrq.py
@@ -0,0 +1,39 @@
+# (C) Matthew Bloch <matthew@bytemark.co.uk> 2004
+
+"""Domain shutdown.
+"""
+import string
+import sys
+import time
+
+from xen.xend.XendClient import server
+from xen.xm.opts import *
+
+DOM0_NAME = 'Domain-0'
+DOM0_ID = '0'
+
+gopts = Opts(use="""[DOM] [letter]
+
+Sends a Linux sysrq to a domain.
+""")
+
+gopts.opt('help', short='h',
+ fn=set_true, default=0,
+ use="Print this help.")
+
+def sysrq(dom, req):
+ server.xend_domain_shutdown(dom, 'sysrq', req)
+
+def main(argv):
+ opts = gopts
+ args = opts.parse(argv)
+ if opts.vals.help:
+ opts.usage()
+ return
+
+ # no options for the moment
+ if len(args) < 1: opts.err('Missing domain')
+ if len(args) < 2: opts.err('Missing sysrq character')
+ dom = args[0]
+ req = ord(args[1][0])
+ sysrq(dom, req)
diff --git a/tools/sv/Makefile b/tools/sv/Makefile
index c7bd3d5880..4da91e0674 100755
--- a/tools/sv/Makefile
+++ b/tools/sv/Makefile
@@ -1,49 +1,34 @@
-sv_insdir := $(prefix)/var/xen/sv
+sv_insdir := /var/lib/xen/sv
+INSTALL = install
+INSTALL_DIR = $(INSTALL) -d -m0755
+INSTALL_DATA = $(INSTALL) -m0644
all:
+IMAGES = xen.png orb_01.jpg orb_02.jpg
+IMAGES += left-end-highlight.jpg left-end-no-highlight.jpg
+IMAGES += right-end-highlight.jpg right-end-no-highlight.jpg
+IMAGES += middle-highlight.jpg middle-no-highlight.jpg
+IMAGES += seperator.jpg
+IMAGES += seperator-left-highlight.jpg seperator-right-highlight.jpg
+IMAGES += shutdown.png reboot.png pause.png unpause.png destroy.png
+IMAGES += small-destroy.png small-pause.png small-unpause.png
+IMAGES += next.png previous.png finish.png
+
install:
- # make parent directory
- mkdir -p $(sv_insdir)
-
- # copy Main.rpy file - references xen.sv.Main class
- install -m0644 Main.rpy $(sv_insdir)
+ # copy XenSV Main.rpy file
+ @[ -d $(DESTDIR)$(sv_insdir) ] || $(INSTALL_DIR) $(DESTDIR)$(sv_insdir)
+ @$(INSTALL_DATA) Main.rpy $(DESTDIR)$(sv_insdir)
- # make images folder
- mkdir -p $(sv_insdir)/images
-
- # copy images
- install -m0644 images/xen.png $(sv_insdir)/images
- install -m0644 images/orb_01.jpg $(sv_insdir)/images
- install -m0644 images/orb_02.jpg $(sv_insdir)/images
-
- install -m0644 images/left-end-highlight.jpg $(sv_insdir)/images
- install -m0644 images/left-end-no-highlight.jpg $(sv_insdir)/images
- install -m0644 images/right-end-highlight.jpg $(sv_insdir)/images
- install -m0644 images/right-end-no-highlight.jpg $(sv_insdir)/images
+ # copy XenSV images
+ @[ -d $(DESTDIR)$(sv_insdir)/images ] || \
+ $(INSTALL_DIR) $(DESTDIR)$(sv_insdir)/images
+ @(cd images && $(INSTALL_DATA) $(IMAGES) $(DESTDIR)$(sv_insdir)/images)
- install -m0644 images/middle-highlight.jpg $(sv_insdir)/images
- install -m0644 images/middle-no-highlight.jpg $(sv_insdir)/images
-
- install -m0644 images/seperator.jpg $(sv_insdir)/images
- install -m0644 images/seperator-left-highlight.jpg $(sv_insdir)/images
- install -m0644 images/seperator-right-highlight.jpg $(sv_insdir)/images
-
- install -m0644 images/shutdown.png $(sv_insdir)/images
- install -m0644 images/reboot.png $(sv_insdir)/images
- install -m0644 images/pause.png $(sv_insdir)/images
- install -m0644 images/unpause.png $(sv_insdir)/images
-
- install -m0644 images/next.png $(sv_insdir)/images
- install -m0644 images/previous.png $(sv_insdir)/images
- install -m0644 images/finish.png $(sv_insdir)/images
-
- # make include folder
- mkdir -p $(sv_insdir)/inc
-
- # copy stylesheet
- install -m0644 inc/style.css $(sv_insdir)/inc
- install -m0644 inc/script.js $(sv_insdir)/inc
+ # copy XenSV stylesheet
+ @[ -d $(DESTDIR)$(sv_insdir)/inc ] || \
+ $(INSTALL_DIR) $(DESTDIR)$(sv_insdir)/inc
+ @$(INSTALL_DATA) inc/style.css inc/script.js $(DESTDIR)$(sv_insdir)/inc
clean:
diff --git a/tools/sv/images/destroy.png b/tools/sv/images/destroy.png
new file mode 100644
index 0000000000..9545fc4837
--- /dev/null
+++ b/tools/sv/images/destroy.png
Binary files differ
diff --git a/tools/sv/images/small-destroy.png b/tools/sv/images/small-destroy.png
new file mode 100644
index 0000000000..f800bd7685
--- /dev/null
+++ b/tools/sv/images/small-destroy.png
Binary files differ
diff --git a/tools/sv/images/small-pause.png b/tools/sv/images/small-pause.png
new file mode 100644
index 0000000000..7bbdbfaafe
--- /dev/null
+++ b/tools/sv/images/small-pause.png
Binary files differ
diff --git a/tools/sv/images/small-unpause.png b/tools/sv/images/small-unpause.png
new file mode 100644
index 0000000000..6ae5687a0c
--- /dev/null
+++ b/tools/sv/images/small-unpause.png
Binary files differ
diff --git a/tools/sv/inc/script.js b/tools/sv/inc/script.js
index fca5a972ab..47cd10399c 100755
--- a/tools/sv/inc/script.js
+++ b/tools/sv/inc/script.js
@@ -13,3 +13,10 @@ function doOp( op )
document.forms[0].op.value = op
document.forms[0].submit()
}
+
+function doOp2( op, args )
+{
+ document.forms[0].op.value = op
+ document.forms[0].args.value = args
+ document.forms[0].submit()
+}
diff --git a/tools/vnet/00README b/tools/vnet/00README
new file mode 100644
index 0000000000..a239e26b12
--- /dev/null
+++ b/tools/vnet/00README
@@ -0,0 +1,10 @@
+This directory contains the implementation of vnets:
+virtual private networks for virtual machines.
+See doc/ for more information and examples/ for example
+configurations.
+
+The kernel module is in vnet-module/ and the vnet forwarding
+daemon is in vnetd/. The vnetd daemon makes vnets work across
+subnets when multicast routing is not available.
+
+Mike Wray <mike.wray@hp.com> \ No newline at end of file
diff --git a/tools/vnet/INSTALL b/tools/vnet/INSTALL
new file mode 100644
index 0000000000..8ece553116
--- /dev/null
+++ b/tools/vnet/INSTALL
@@ -0,0 +1,31 @@
+To compile and install run "make install"; if it fails or you need to reinstall
+run "make clean" first or the build will fail, at least that is what I have
+found under 2.6.10.
+
+Other important items:
+1) You will need to have your xen0 kernel compiled with HMAC_SUPPORT
+ 2.6.x = (MAIN MENU: Cryptographic Options -> HMAC Support)
+ BEFORE running "make install".
+
+2) You will want at least some of the other alogorithms listed under
+ "Cryptographic Options" for the kernel compiled as modules.
+
+3) You will want the networking IPsec/VLAN options compiled in as modules
+ 2.6.x = (MAIN MENU: Device Drivers -> Networking Support ->
+ Networking Options ->
+ IP: AH transformation
+ IP: ESP transformation
+ IP: IPComp transformation
+ IP: tunnel transformation
+
+ IPsec user configuration interface
+
+ 802.1Q VLAN Support
+
+4) The module (vnet_module) will not properly load from the command line
+ with a "modprobe vnet_module". Use network-vnet to properly configure
+ your system and load the module for you.
+
+Please refer to the additional documentation found in tools/vnet/doc for
+proper syntax and config file parameters.
+
diff --git a/tools/vnet/Makefile b/tools/vnet/Makefile
new file mode 100644
index 0000000000..d4d8183130
--- /dev/null
+++ b/tools/vnet/Makefile
@@ -0,0 +1,51 @@
+
+export LINUX_SERIES ?=2.6
+
+# Root path to install in.
+# Set to '/' to install relative to filesystem root.
+export prefix?=$(shell cd ../../dist/install && pwd)
+
+.PHONY: all compile
+.PHONY: gc-install gc-clean gc-prstine
+.PHONY: vnetd vnet-module install dist clean pristine
+
+all: compile
+
+compile: vnetd vnet-module
+#compile: vnet-module
+
+gc.tar.gz:
+ wget http://www.hpl.hp.com/personal/Hans_Boehm/gc/gc_source/$@
+
+gc: gc.tar.gz
+ tar xfz gc.tar.gz
+ ln -sf gc?.? gc
+
+gc-install: gc
+ (cd gc && make test && ./configure --prefix=`pwd`/install)
+ make -C gc
+ make -C gc install
+
+gc-clean:
+ -$(MAKE) -C gc clean
+
+gc-pristine:
+ -rm -rf gc?.? gc
+
+vnetd: gc-install
+ $(MAKE) -C vnetd
+
+vnet-module:
+ $(MAKE) -C vnet-module
+
+install: compile
+ $(MAKE) -C vnetd install
+ $(MAKE) -C vnet-module install
+ $(MAKE) -C examples install
+
+clean:
+ -$(MAKE) -C vnetd clean
+ -$(MAKE) -C vnet-module clean
+ -rm -rf gc?.? gc
+
+pristine: clean gc-pristine
diff --git a/tools/vnet/doc/vnet-module.txt b/tools/vnet/doc/vnet-module.txt
new file mode 100644
index 0000000000..b9c8ac57c3
--- /dev/null
+++ b/tools/vnet/doc/vnet-module.txt
@@ -0,0 +1,50 @@
+Vnet Module Command Interface
+Mike Wray <mike.wray@hp.com>
+2004/09/17
+
+When insmod the vnet-module creates /proc/vnet/policy which
+can be used to control the module by writing commands into it.
+The return code from the command should be returned by close.
+
+The commands are:
+
+(vnet.add (id <id>) [(security { none | auth | conf } )] )
+
+Create the vnet with id <id> and the given security level (default none).
+Security levels:
+- none: no security
+- auth: message authentication (IPSEC hmac)
+- conf: message confidentiality (IPSEC hmac and encryption)
+
+(vnet.del (id <id>))
+
+Delete the vnet with id <id>.
+
+(vif.add (vnet <vnetid>) (vmac <macaddr>))
+
+Add the vif with MAC address <macaddr> to the vnet with id <vnetid>.
+This makes the vnet module respond to VARP requests for <macaddr>
+on vnet <vnetid>.
+
+(vif.del (vnet <vnetid>) (vmac <macaddr>))
+
+Remove the vif with MAC address <macaddr> from the vnet with id <vnetid>.
+The vnet module will stop responding to VARP for the vif.
+
+Examples:
+
+To create vnet 10 with no security:
+
+echo '(vnet.add (id 10))' > /proc/vnet/policy
+
+To create vnet 11 with message authentication:
+
+echo '(vnet.add (id 11) (security auth))' > /proc/vnet/policy
+
+To add the vif with vmac "aa:00:00:bc:34:ae" to vnet 10:
+
+echo '(vif.add (vnet 10) (vmac aa:00:00:bc:34:ae))' > /proc/vnet/policy
+
+To remove the vif from the vnet:
+
+echo '(vif.del (vnet 10) (vmac aa:00:00:bc:34:ae))' > /proc/vnet/policy
diff --git a/tools/vnet/doc/vnet-xend.txt b/tools/vnet/doc/vnet-xend.txt
new file mode 100644
index 0000000000..9ad1c523d4
--- /dev/null
+++ b/tools/vnet/doc/vnet-xend.txt
@@ -0,0 +1,140 @@
+
+Vnets: Virtual Networks for Virtual Machines
+
+Mike Wray <mike.wray@hp.com>
+
+0) Introduction
+---------------
+
+Vnets provide virtual private LANs for virtual machines.
+This is done using bridging and tunneling. A virtual interface
+on a vnet can only see other interfaces on the same vnet - it cannot
+see the real network, and the real network cannot see it either.
+
+Virtual interfaces on the same vnet can be on the same machine
+or on different machines, they can still talk. The hosting machines
+can even be on different subnets if you run vnetd to forward,
+or have multicast routing enabled.
+
+
+1) Installing vnet support
+--------------------------
+
+Assuming the code has been installed (make install in the parent directory),
+configure xend to use 'network-vnet' instead of the default 'network' to
+start up networking. This just loads the vnet module when networking starts.
+
+In /etc/xend/xend-config.sxp:
+
+Configure the network script:
+
+(network-script network-vnet)
+
+Restart xend.
+
+2) Creating vnets
+-----------------
+
+Xend already implements commands to add/remove vnets and
+bridge to them. To add a vnet use
+
+xm call vnet_add <vnet config file>
+
+For example, if vnet97.sxp contains:
+
+(vnet (id 97) (bridge vnet97) (vnetif vnetif97) (security none))
+
+do
+
+xm call vnet_add vnet97.sxp
+
+This will define a vnet with id 97 and no security. The bridge for the
+vnet is called vnet97 and the virtual interface for it is vnetif97.
+To add an interface on a vm to this vnet simply set its bridge to vnet97
+in its configuration.
+
+In Python:
+
+vif="bridge=vnet97"
+
+In sxp:
+
+(dev (vif (mac aa:00:00:01:02:03) (bridge vnet97)))
+
+Once configured, vnets are persistent in the xend database.
+To remove a vnet use
+
+xm call vnet_delete <vnet id>
+
+To list vnets use
+
+xm call vnets
+
+To get information on a vnet id use
+
+xm call vnet <vnet id>
+
+3) Troubleshooting
+------------------
+
+The vnet module should appear in 'lsmod'.
+If a vnet has been configured it should appear in the output of 'xm call vnets'.
+Its bridge and interface should appear in 'ifconfig'.
+It should also show in 'brctl show', with its attached interfaces.
+
+You can 'see into' a vnet from dom0 if you put an IP address on the bridge.
+For example, if you have vnet97 with a vm with ip addr 10.0.0.12 on it,
+then
+
+ifconfig vnet97 10.0.0.20 up
+
+should let you ping 10.0.0.12 via the vnet97 bridge.
+
+4) Examples
+-----------
+
+Here's the full config for a vm on vnet 97, using ip addr 10.0.0.12:
+
+(vm
+ (name dom12)
+ (memory '64')
+ (cpu '1')
+ (console '8502')
+ (image
+ (linux
+ (kernel /boot/vmlinuz-2.6.9-xenU)
+ (ip 10.0.0.12:1.2.3.4::::eth0:off)
+ (root /dev/hda1)
+ (args 'rw fastboot 4')
+ )
+ )
+ (device (vbd (uname phy:hda2) (dev hda1) (mode w)))
+ (device (vif (mac aa:00:00:11:00:12) (bridge vnet97)))
+)
+
+If you run another vm on the same vnet:
+
+(vm
+ (name dom11)
+ (memory '64')
+ (cpu '1')
+ (console '8501')
+ (image
+ (linux
+ (kernel /boot/vmlinuz-2.6.9-xenU)
+ (ip 10.0.0.11:1.2.3.4::::eth0:off)
+ (root /dev/hda1)
+ (args 'rw fastboot 4')
+ )
+ )
+ (device (vbd (uname phy:hda3) (dev hda1) (mode w)))
+ (device (vif (mac aa:00:00:11:00:11) (bridge vnet97)))
+)
+
+the vms should be able to talk over the vnet. Check with ping.
+If they are both on the same machine the connection will simply
+be the vnet97 bridge, if they are on separate machines their
+packets will be tunneled in etherip. They should be able to
+see each other, but not the real network.
+
+
diff --git a/tools/vnet/examples/Makefile b/tools/vnet/examples/Makefile
new file mode 100644
index 0000000000..d0cc4a6e57
--- /dev/null
+++ b/tools/vnet/examples/Makefile
@@ -0,0 +1,12 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+
+XEN_SCRIPT_DIR:=/etc/xen/scripts
+
+all:
+
+install:
+ install -m 0755 -d $(DESTDIR)$(XEN_SCRIPT_DIR)
+ install -m 0554 network-vnet $(DESTDIR)$(XEN_SCRIPT_DIR)
+
+clean: \ No newline at end of file
diff --git a/tools/vnet/examples/network-vnet b/tools/vnet/examples/network-vnet
new file mode 100755
index 0000000000..4b388bd3f9
--- /dev/null
+++ b/tools/vnet/examples/network-vnet
@@ -0,0 +1,218 @@
+#!/bin/sh
+#============================================================================
+# Default Xen network start/stop script.
+# Xend calls a network script when it starts.
+# The script name to use is defined in /etc/xen/xend-config.sxp
+# in the network-script field.
+#
+# This script creates a bridge (default xen-br0), adds a device
+# (default eth0) to it, copies the IP addresses from the device
+# to the bridge and adjusts the routes accordingly.
+#
+# If all goes well, this should ensure that networking stays up.
+# However, some configurations are upset by this, especially
+# NFS roots. If the bridged setup does not meet your needs,
+# configure a different script, for example using routing instead.
+#
+# Usage:
+#
+# network (start|stop|status) {VAR=VAL}*
+#
+# Vars:
+#
+# bridge The bridge to use (default xen-br0).
+# netdev The interface to add to the bridge (default eth0).
+# antispoof Whether to use iptables to prevent spoofing (default yes).
+#
+# start:
+# Creates the bridge and enslaves netdev to it.
+# Copies the IP addresses from netdev to the bridge.
+# Deletes the routes to netdev and adds them on bridge.
+#
+# stop:
+# Removes netdev from the bridge.
+# Deletes the routes to bridge and adds them to netdev.
+#
+# status:
+# Print ifconfig for netdev and bridge.
+# Print routes.
+#
+#============================================================================
+
+# Exit if anything goes wrong.
+set -e
+
+# First arg is the operation.
+OP=$1
+shift
+
+# Pull variables in args in to environment.
+for arg ; do export "${arg}" ; done
+
+bridge=${bridge:-xen-br0}
+netdev=${netdev:-eth0}
+antispoof=${antispoof:-yes}
+
+echo "network $OP bridge=$bridge netdev=$netdev antispoof=$antispoof"
+
+# Usage: transfer_addrs src dst
+# Copy all IP addresses (including aliases) from device $src to device $dst.
+transfer_addrs () {
+ local src=$1
+ local dst=$2
+ # Don't bother if $dst already has IP addresses.
+ if ip addr show dev ${dst} | egrep -q '^ *inet' ; then
+ return
+ fi
+ # Address lines start with 'inet' and have the device in them.
+ # Replace 'inet' with 'ip addr add' and change the device name $src
+ # to 'dev $src'. Remove netmask as we'll add routes later.
+ ip addr show dev ${src} | egrep '^ *inet' | sed -e "
+s/inet/ip addr add/
+s@\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)/[0-9]\+@\1@
+s/${src}/dev ${dst}/
+" | sh -e
+}
+
+# Usage: transfer_routes src dst
+# Get all IP routes to device $src, delete them, and
+# add the same routes to device $dst.
+# The original routes have to be deleted, otherwise adding them
+# for $dst fails (duplicate routes).
+transfer_routes () {
+ local src=$1
+ local dst=$2
+ # List all routes and grep the ones with $src in.
+ # Stick 'ip route del' on the front to delete.
+ # Change $src to $dst and use 'ip route add' to add.
+ ip route list | grep ${src} | sed -e "
+h
+s/^/ip route del /
+P
+g
+s/${src}/${dst}/
+s/^/ip route add /
+P
+d
+" | sh -e
+}
+
+# Usage: create_bridge dev bridge
+# Create bridge $bridge and add device $dev to it.
+create_bridge () {
+ local dev=$1
+ local bridge=$2
+
+ # Don't create the bridge if it already exists.
+ if ! brctl show | grep -q ${bridge} ; then
+ brctl addbr ${bridge}
+ brctl stp ${bridge} off
+ brctl setfd ${bridge} 0
+ fi
+ ifconfig ${bridge} up
+}
+
+# Usage: antispoofing dev bridge
+# Set the default forwarding policy for $dev to drop.
+# Allow forwarding to the bridge.
+antispoofing () {
+ local dev=$1
+ local bridge=$2
+
+ iptables -P FORWARD DROP
+ iptables -A FORWARD -m physdev --physdev-in ${dev} -j ACCEPT
+}
+
+# Usage: show_status dev bridge
+# Print ifconfig and routes.
+show_status () {
+ local dev=$1
+ local bridge=$2
+
+ echo '============================================================'
+ ifconfig ${dev}
+ ifconfig ${bridge}
+ echo ' '
+ ip route list
+ echo ' '
+ route -n
+ echo '============================================================'
+}
+
+# Insert the vnet module if it can be found and
+# it's not already there.
+vnet_insert () {
+ local module="vnet_module"
+ local mod_dir=/lib/modules/$(uname -r)/kernel
+ local mod_path="${mod_dir}/${module}"
+ local mod_obj=""
+
+ for ext in ".o" ".ko" ; do
+ f=${mod_path}${ext}
+ if [ -f ${f} ] ; then
+ mod_obj=$f
+ break
+ fi
+ done
+ if [ "${mod_obj}" == "" ] ; then
+ return
+ fi
+ if lsmod | grep -q ${module} ; then
+ echo "VNET: ${module} loaded"
+ else
+ echo "VNET: Loading ${module}..."
+ insmod ${mod_obj}
+ fi
+}
+
+op_start () {
+ if [ "${bridge}" == "null" ] ; then
+ return
+ fi
+ # Create the bridge and give it the interface IP addresses.
+ # Move the interface routes onto the bridge.
+ create_bridge ${netdev} ${bridge}
+ transfer_addrs ${netdev} ${bridge}
+ transfer_routes ${netdev} ${bridge}
+ # Don't add $dev to $bridge if it's already on a bridge.
+ if ! brctl show | grep -q ${netdev} ; then
+ brctl addif ${bridge} ${netdev}
+ fi
+
+ if [ ${antispoof} == 'yes' ] ; then
+ antispoofing ${netdev} ${bridge}
+ fi
+
+ vnet_insert
+}
+
+op_stop () {
+ if [ "${bridge}" == "null" ] ; then
+ return
+ fi
+ # Remove the interface from the bridge.
+ # Move the routes back to the interface.
+ brctl delif ${bridge} ${netdev}
+ transfer_routes ${bridge} ${netdev}
+
+ # It's not our place to be enabling forwarding...
+}
+
+case ${OP} in
+ start)
+ op_start
+ ;;
+
+ stop)
+ op_stop
+ ;;
+
+ status)
+ show_status ${netdev} ${bridge}
+ ;;
+
+ *)
+ echo 'Unknown command: ' ${OP}
+ echo 'Valid commands are: start, stop, status'
+ exit 1
+esac
diff --git a/tools/vnet/examples/vnet97.sxp b/tools/vnet/examples/vnet97.sxp
new file mode 100644
index 0000000000..ef0784369b
--- /dev/null
+++ b/tools/vnet/examples/vnet97.sxp
@@ -0,0 +1,3 @@
+# Vnet configuration for a vnet with id 97 and no security.
+# Configure using 'xm call vnet_add vnet97.sxp'.
+(vnet (id 97) (bridge vnet97) (vnetif vnetif97) (security none))
diff --git a/tools/vnet/examples/vnet98.sxp b/tools/vnet/examples/vnet98.sxp
new file mode 100644
index 0000000000..807d56daaf
--- /dev/null
+++ b/tools/vnet/examples/vnet98.sxp
@@ -0,0 +1,3 @@
+# Vnet configuration for a vnet with id 98 and message authentication.
+# Configure using 'xm call vnet_add vnet98.sxp'.
+(vnet (id 98) (bridge vnet98) (vnetif vnetif98) (security auth))
diff --git a/tools/vnet/examples/vnet99.sxp b/tools/vnet/examples/vnet99.sxp
new file mode 100644
index 0000000000..ffce1d7fbf
--- /dev/null
+++ b/tools/vnet/examples/vnet99.sxp
@@ -0,0 +1,3 @@
+# Vnet configuration for a vnet with id 99 and message confidentiality.
+# Configure using 'xm call vnet_add vnet99.sxp'.
+(vnet (id 99) (bridge vnet99) (vnetif vnetif99) (security conf))
diff --git a/tools/vnet/vnet-module/00README b/tools/vnet/vnet-module/00README
new file mode 100644
index 0000000000..3a798985b3
--- /dev/null
+++ b/tools/vnet/vnet-module/00README
@@ -0,0 +1,39 @@
+Vnet module for network virtualization.
+Mike Wray <mike.wray@hp.com>
+
+*) Compiling
+The vnet module can be compiled for 2.4 or 2.6 series kernels.
+The makefiles use the following variables, which
+can be set in your env or on the make command line:
+
+LINUX_SERIES: linux release to compile for, 2.4 (default), or 2.6.
+XEN_ROOT: root of the xen tree containing kernel source.
+KERNEL_VERSION: kernel version, default got from XEN_ROOT.
+KERNEL_MINOR: kernel minor version, default -xen0.
+KERNEL_SRC: path to kernel source, default linux-<VERSION> under XEN_ROOT.
+
+*) For 2.4 kernel
+
+To compile from scratch:
+
+make clean
+make LINUX_SERIES=2.4
+
+This will build vnet_module.o in the current directory.
+To install the module use
+
+make LINUX_SERIES=2.4 install
+
+*) For 2.6 kernel
+
+To compile from scratch:
+
+make clean
+make
+
+This will build vnet_module.ko in the current directory.
+To install the module use
+
+make install
+
+
diff --git a/tools/vnet/vnet-module/Makefile b/tools/vnet/vnet-module/Makefile
new file mode 100644
index 0000000000..9d33c70981
--- /dev/null
+++ b/tools/vnet/vnet-module/Makefile
@@ -0,0 +1,67 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+#
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free software Foundation, Inc.,
+# 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+
+#============================================================================
+ifeq ($(src),)
+LINUX_SERIES ?=2.6
+
+include Makefile-$(LINUX_SERIES)
+
+#============================================================================
+else
+#============================================================================
+# This section is for the 2.6 kbuild.
+
+#$(warning KBUILD_EXTMOD $(KBUILD_EXTMOD))
+#$(warning src $(src))
+#$(warning obj $(obj))
+
+include $(src)/Makefile.vnet
+
+obj-m = vnet_module.o
+vnet_module-objs = $(VNET_OBJ)
+vnet_module-objs += $(VNET_LIB_OBJ)
+
+#----------------------------------------------------------------------------
+# The fancy stuff in the kernel build defeats 'vpath %.c' so we can't
+# use that to get the lib files compiled.
+# Setup explicit rules for them using the kbuild C compile rule.
+
+# File names in the lib dir.
+remote_srcs = $(foreach file,$(VNET_LIB_SRC),$(LIB_DIR)/$(file))
+
+# Equivalent file names here.
+local_srcs = $(foreach file,$(VNET_LIB_SRC),$(src)/$(file))
+
+# Objects for the local names.
+local_objs = $(local_srcs:.c=.o)
+
+# Make the local objects depend on compiling the remote sources.
+$(local_objs): $(src)/%.o: $(LIB_DIR)/%.c
+ $(call if_changed_rule,cc_o_c)
+#----------------------------------------------------------------------------
+
+vpath %.h $(LIB_DIR)
+EXTRA_CFLAGS += -I $(LIB_DIR)
+EXTRA_CFLAGS += -I $(src)
+
+endif
+#============================================================================
+
diff --git a/tools/vnet/vnet-module/Makefile-2.4 b/tools/vnet/vnet-module/Makefile-2.4
new file mode 100644
index 0000000000..5781fca097
--- /dev/null
+++ b/tools/vnet/vnet-module/Makefile-2.4
@@ -0,0 +1,97 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+#
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free software Foundation, Inc.,
+# 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+
+#============================================================================
+# Vnet module makefile for 2.4 series kernels.
+
+LINUX_SERIES ?=2.4
+include Makefile.ver
+
+KERNEL_MODULE := vnet_module.o
+
+CONFIG_MODVERSIONS := $(shell grep 'CONFIG_MODVERSIONS=y' $(KERNEL_SRC)/.config && echo 1 || echo 0)
+
+include Makefile.vnet
+
+VNET_OBJ += $(VNET_LIB_OBJ)
+
+#----------------------------------------------------------------------------
+
+vpath %.h $(KERNEL_SRC)/include
+INCLUDES+= -I $(KERNEL_SRC)/include
+
+vpath %.h $(LIB_DIR)
+vpath %.c $(LIB_DIR)
+INCLUDES += -I $(LIB_DIR)
+
+INCLUDES+= -I .
+
+#----------------------------------------------------------------------------
+
+CPPFLAGS += -D__KERNEL__
+CPPFLAGS += -DMODULE
+
+ifeq ($(CONFIG_MODVERSIONS), 1)
+CPPFLAGS += -DMODVERSIONS
+CPPFLAGS += -include $(KERNEL_SRC)/include/linux/modversions.h
+endif
+
+CPPFLAGS += $(INCLUDES)
+
+CFLAGS += -Wall
+CFLAGS += -Wstrict-prototypes
+CFLAGS += -Wno-trigraphs
+CFLAGS += -Wno-unused-function
+CFLAGS += -Wno-unused-parameter
+
+CFLAGS += -O2
+CFLAGS += -fno-strict-aliasing
+CFLAGS += -fno-common
+#CFLAGS += -fomit-frame-pointer
+
+# Dependencies. Gcc generates them for us.
+CFLAGS += -Wp,-MD,.$(@F).d
+VNET_DEP = .*.d
+#----------------------------------------------------------------------------
+
+.PHONY: all
+all: module
+
+.PHONY: module modules
+module modules: $(KERNEL_MODULE)
+
+$(KERNEL_MODULE): $(VNET_OBJ)
+ $(LD) -r -o $@ $^
+
+.PHONY: install install-module modules_install
+install install-module modules_install: module
+ install -m 0755 -d $(DESTDIR)$(KERNEL_MODULE_DIR)
+ install -m 0554 $(KERNEL_MODULE) $(DESTDIR)$(KERNEL_MODULE_DIR)
+
+TAGS:
+ etags *.c *.h
+
+.PHONY: clean
+clean:
+ @rm -f *.a *.o *.ko *~
+ @rm -f $(VNET_DEP) .*.cmd *.mod.?
+ @rm -rf .tmp_versions
+
+-include $(VNET_DEP)
diff --git a/tools/vnet/vnet-module/Makefile-2.6 b/tools/vnet/vnet-module/Makefile-2.6
new file mode 100644
index 0000000000..053391e572
--- /dev/null
+++ b/tools/vnet/vnet-module/Makefile-2.6
@@ -0,0 +1,51 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+#
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free software Foundation, Inc.,
+# 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+
+#============================================================================
+# Vnet module makefile for 2.6 series kernels.
+
+LINUX_SERIES ?=2.6
+include Makefile.ver
+
+KERNEL_MODULE = vnet_module.ko
+
+#----------------------------------------------------------------------------
+#export KBUILD_VERBOSE=1
+
+.PHONY: all
+all: module
+
+.PHONY: module
+module modules:
+ $(MAKE) -C $(KERNEL_SRC) M=`pwd` modules
+
+.PHONY: install install-module modules_install
+install install-module modules_install: module
+ install -m 0755 -d $(DESTDIR)$(KERNEL_MODULE_DIR)/xen
+ install -m 0554 $(KERNEL_MODULE) $(DESTDIR)$(KERNEL_MODULE_DIR)/xen
+
+.PHONY: clean
+clean:
+ @$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
+ @rm -f *.a *.o *.ko *~ .*.d .*.cmd *.mod.?
+
+TAGS:
+ etags *.c *.h
+
diff --git a/tools/vnet/vnet-module/Makefile.ver b/tools/vnet/vnet-module/Makefile.ver
new file mode 100644
index 0000000000..586c26ee8e
--- /dev/null
+++ b/tools/vnet/vnet-module/Makefile.ver
@@ -0,0 +1,49 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+#
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free software Foundation, Inc.,
+# 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+
+#----------------------------------------------------------------------------
+# Xeno/xen.
+
+# Root of xen tree.
+XEN_ROOT ?=../../..
+
+# Path to relativize the install. Set to /
+# to install relative to filesystem root.
+prefix ?=$(XEN_ROOT)/install/
+#----------------------------------------------------------------------------
+
+LINUX_SERIES ?=2.6
+KERNEL_MINOR ?=-xen0
+
+LINUX_VERSION ?= $(shell ( /bin/ls -ld $(XEN_ROOT)/linux-$(LINUX_SERIES).*-xen-sparse ) 2>/dev/null | \
+ sed -e 's!^.*linux-\(.\+\)-xen-sparse!\1!' )
+
+ifeq ($(LINUX_VERSION),)
+$(error Kernel source for linux $(LINUX_SERIES) not found)
+endif
+
+KERNEL_VERSION =$(LINUX_VERSION)$(KERNEL_MINOR)
+
+KERNEL_SRC ?= $(XEN_ROOT)/linux-$(KERNEL_VERSION)
+
+KERNEL_MODULE_DIR = /lib/modules/$(KERNEL_VERSION)/kernel
+
+#$(warning KERNEL_VERSION $(KERNEL_VERSION))
+#$(warning KERNEL_SRC $(KERNEL_SRC))
diff --git a/tools/vnet/vnet-module/Makefile.vnet b/tools/vnet/vnet-module/Makefile.vnet
new file mode 100644
index 0000000000..366c2fc9b9
--- /dev/null
+++ b/tools/vnet/vnet-module/Makefile.vnet
@@ -0,0 +1,57 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+#
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free software Foundation, Inc.,
+# 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+#============================================================================
+
+ifeq ($(src),)
+SRC_DIR=
+else
+SRC_DIR=$(src)/
+endif
+
+LIB_DIR := $(SRC_DIR)../../libxutil
+
+VNET_SRC :=
+VNET_SRC += esp.c
+VNET_SRC += etherip.c
+VNET_SRC += random.c
+VNET_SRC += sa_algorithm.c
+VNET_SRC += sa.c
+VNET_SRC += skb_context.c
+VNET_SRC += skb_util.c
+VNET_SRC += tunnel.c
+VNET_SRC += varp.c
+VNET_SRC += varp_socket.c
+VNET_SRC += vif.c
+VNET_SRC += vnet.c
+VNET_SRC += vnet_dev.c
+VNET_SRC += vnet_ioctl.c
+
+VNET_LIB_SRC += allocate.c
+VNET_LIB_SRC += enum.c
+VNET_LIB_SRC += hash_table.c
+VNET_LIB_SRC += iostream.c
+VNET_LIB_SRC += kernel_stream.c
+VNET_LIB_SRC += sxpr.c
+VNET_LIB_SRC += sxpr_parser.c
+VNET_LIB_SRC += sys_net.c
+VNET_LIB_SRC += sys_string.c
+
+VNET_OBJ := $(VNET_SRC:.c=.o)
+VNET_LIB_OBJ := $(VNET_LIB_SRC:.c=.o)
+
diff --git a/tools/vnet/vnet-module/esp.c b/tools/vnet/vnet-module/esp.c
new file mode 100644
index 0000000000..7e27006835
--- /dev/null
+++ b/tools/vnet/vnet-module/esp.c
@@ -0,0 +1,863 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <asm/uaccess.h>
+
+#include <linux/init.h>
+
+#include <linux/version.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+
+#include <linux/if_ether.h>
+#include <linux/icmp.h>
+
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
+#include <linux/random.h>
+
+#include <esp.h>
+#include <sa.h>
+#include <sa_algorithm.h>
+#include <tunnel.h>
+#include <vnet.h>
+#include <skb_util.h>
+
+static const int DEBUG_ICV = 0;
+
+#define MODULE_NAME "IPSEC"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/* Outgoing packet: [ eth | ip | data ]
+ * After etherip: [ eth2 | ip2 | ethip | eth | ip | data ]
+ * After esp : [ eth2 | ip2 | esp | {ethip | eth | ip | data} | pad | icv ]
+ * ^ +
+ * The curly braces { ... } denote encryption.
+ * The esp header includes the fixed esp headers and the iv (variable size).
+ * The point marked ^ does not move. To the left is in the header, to the right
+ * is in the frag. Remember that all outgoing skbs (from domains) have 1 frag.
+ * Data after + is added by esp, using an extra frag.
+ *
+ * Incoming as above.
+ * After decrypt: [ eth2 | ip2 | esp | ethip | eth | ip | data | pad | icv ]
+ * Trim tail: [ eth2 | ip2 | esp | ethip | eth | ip | data ]
+ * Drop hdr: [ eth2 | ip2 | ethip | eth | ip | data ]
+ * ^
+ * The point marked ^ does not move. Incoming skbs are linear (no frags).
+ * The tail is trimmed by adjusting skb->tail and len.
+ * The esp hdr is dropped by using memmove to move the headers and
+ * adjusting the skb pointers.
+ *
+ * todo: Now this code is in linux we can't assume 1 frag for outbound skbs,
+ * or (maybe) that memmove is safe on inbound.
+ */
+
+/** Round n up to a multiple of block.
+ * If block is less than 2 does nothing.
+ * Otherwise assume block is a power of 2.
+ *
+ * @param n to round up
+ * @param block size to round to a multiple of
+ * @return rounded value
+ */
+static inline int roundup(int n, int block){
+ if(block <= 1) return n;
+ block--;
+ return (n + block) & ~block;
+}
+
+/** Check if n is a multiple of block.
+ * If block is less than 2 returns 1.
+ * Otherwise assumes block is a power of 2.
+ *
+ * @param n to check
+ * @param block block size
+ * @return 1 if a multiple, 0 otherwise
+ */
+static inline int multipleof(int n, int block){
+ if(block <= 1) return 1;
+ block--;
+ return !(n & block);
+}
+
+/** Convert from bits to bytes.
+ *
+ * @param n number of bits
+ * @return number of bytes
+ */
+static inline int bits_to_bytes(int n){
+ return n / 8;
+}
+
+
+/** Insert esp padding at the end of an skb.
+ * Inserts padding bytes, number of padding bytes, protocol number.
+ *
+ * @param skb skb
+ * @param offset offset from skb end to where padding should end
+ * @param extra_n total amount of padding
+ * @param protocol protocol number (from original ip hdr)
+ * @return 0 on success, error code otherwise
+ */
+static int esp_sa_pad(struct sk_buff *skb, int offset, int extra_n,
+ unsigned char protocol){
+ int err;
+ char *data;
+ int pad_n = extra_n - ESP_PAD_N;
+ int i;
+ char buf[extra_n];
+
+ data = buf;
+ for(i = 1; i <= pad_n; i++){
+ *data++ = i;
+ }
+ *data++ = pad_n;
+ *data++ = protocol;
+ err = skb_put_bits(skb, skb->len - offset - extra_n, buf, extra_n);
+ return err;
+}
+
+/** Encrypt skb. Skips esp header and iv.
+ * Assumes skb->data points at esp header.
+ *
+ * @param esp esp state
+ * @parm esph esp header
+ * @param skb packet
+ * @param head_n size of esp header and iv
+ * @param iv_n size of iv
+ * @param text_n size of ciphertext
+ * @return 0 on success, error code otherwise
+ */
+static int esp_sa_encrypt(ESPState *esp, ESPHdr *esph, struct sk_buff *skb,
+ int head_n, int iv_n, int text_n){
+ int err = 0;
+ int sg_n = skb_shinfo(skb)->nr_frags + 1;
+ struct scatterlist sg[sg_n];
+
+ err = skb_scatterlist(skb, sg, &sg_n, head_n, text_n);
+ if(err) goto exit;
+ if(iv_n){
+ crypto_cipher_set_iv(esp->cipher.tfm, esp->cipher.iv, iv_n);
+ }
+ crypto_cipher_encrypt(esp->cipher.tfm, sg, sg, text_n);
+ if(iv_n){
+ memcpy(esph->data, esp->cipher.iv, iv_n);
+ crypto_cipher_get_iv(esp->cipher.tfm, esp->cipher.iv, iv_n);
+ }
+ exit:
+ return err;
+}
+
+/** Decrypt skb. Skips esp header and iv.
+ * Assumes skb->data points at esp header.
+ *
+ * @param esp esp state
+ * @parm esph esp header
+ * @param skb packet
+ * @param head_n size of esp header and iv
+ * @param iv_n size of iv
+ * @param text_n size of ciphertext
+ * @return 0 on success, error code otherwise
+ */
+static int esp_sa_decrypt(ESPState *esp, ESPHdr *esph, struct sk_buff *skb,
+ int head_n, int iv_n, int text_n){
+ int err = 0;
+ int sg_n = skb_shinfo(skb)->nr_frags + 1;
+ struct scatterlist sg[sg_n];
+
+ err = skb_scatterlist(skb, sg, &sg_n, head_n, text_n);
+ if(err) goto exit;
+ if(iv_n){
+ crypto_cipher_set_iv(esp->cipher.tfm, esph->data, iv_n);
+ }
+ crypto_cipher_decrypt(esp->cipher.tfm, sg, sg, text_n);
+ exit:
+ return err;
+}
+
+/** Compute icv. Includes esp header, iv and ciphertext.
+ * Assumes skb->data points at esp header.
+ *
+ * @param esp esp state
+ * @param skb packet
+ * @param digest_n number of bytes to digest
+ * @param icv_n size of icv
+ * @return 0 on success, error code otherwise
+ */
+static int esp_sa_digest(ESPState *esp, struct sk_buff *skb, int digest_n, int icv_n){
+ int err = 0;
+ u8 icv[icv_n];
+
+ if(DEBUG_ICV){
+ dprintf("> skb digest_n=%d icv_n=%d\n", digest_n, icv_n);
+ skb_print_bits(skb, 0, digest_n);
+ }
+ memset(icv, 0, icv_n);
+ esp->digest.icv(esp, skb, 0, digest_n, icv);
+ skb_put_bits(skb, digest_n, icv, icv_n);
+ return err;
+}
+
+/** Check the icv and trim it from the skb tail.
+ *
+ * @param sa sa state
+ * @param esp esp state
+ * @param esph esp header
+ * @param skb packet
+ * @return 0 on success, error code otherwise
+ */
+static int esp_check_icv(SAState *sa, ESPState *esp, ESPHdr *esph, struct sk_buff *skb){
+ int err = 0;
+ int icv_n = esp->digest.icv_n;
+ int digest_n = skb->len - icv_n;
+ u8 icv_skb[icv_n];
+ u8 icv_new[icv_n];
+
+ dprintf(">\n");
+ if(DEBUG_ICV){
+ dprintf("> skb len=%d digest_n=%d icv_n=%d\n",
+ skb->len, digest_n, icv_n);
+ skb_print_bits(skb, 0, skb->len);
+ }
+ if(skb_copy_bits(skb, digest_n, icv_skb, icv_n)){
+ wprintf("> Error getting icv from skb\n");
+ goto exit;
+ }
+ esp->digest.icv(esp, skb, 0, digest_n, icv_new);
+ if(DEBUG_ICV){
+ dprintf("> len=%d icv_n=%d", digest_n, icv_n);
+ printk("\nskb="); buf_print(icv_skb, icv_n);
+ printk("new="); buf_print(icv_new, icv_n);
+ }
+ if(unlikely(memcmp(icv_new, icv_skb, icv_n))){
+ wprintf("> ICV check failed!\n");
+ err = -EINVAL;
+ sa->counts.integrity_failures++;
+ goto exit;
+ }
+ skb_trim_tail(skb, icv_n);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Send a packet via an ESP SA.
+ *
+ * @param sa SA state
+ * @param skb packet to send
+ * @param tunnel underlying tunnel
+ * @return 0 on success, negative error code otherwise
+ */
+static int esp_sa_send(SAState *sa, struct sk_buff *skb, Tunnel *tunnel){
+ int err = 0;
+ int ip_n; // Size of ip header.
+ int plaintext_n; // Size of plaintext.
+ int ciphertext_n; // Size of ciphertext (including padding).
+ int extra_n; // Extra bytes needed for ciphertext.
+ int icv_n = 0; // Size of integrity check value (icv).
+ int iv_n = 0; // Size of initialization vector (iv).
+ int head_n; // Size of esp header and iv.
+ int tail_n; // Size of esp trailer: padding and icv.
+ ESPState *esp;
+ ESPHdr *esph;
+
+ dprintf(">\n");
+ esp = sa->data;
+ ip_n = (skb->nh.iph->ihl << 2);
+ // Assuming skb->data points at ethernet header, exclude ethernet
+ // header and IP header.
+ plaintext_n = skb->len - ETH_HLEN - ip_n;
+ // Add size of padding fields.
+ ciphertext_n = roundup(plaintext_n + ESP_PAD_N, esp->cipher.block_n);
+ if(esp->cipher.pad_n > 0){
+ ciphertext_n = roundup(ciphertext_n, esp->cipher.pad_n);
+ }
+ extra_n = ciphertext_n - plaintext_n;
+ iv_n = esp->cipher.iv_n;
+ icv_n = esp->digest.icv_n;
+ dprintf("> len=%d plaintext=%d ciphertext=%d extra=%d\n",
+ skb->len, plaintext_n, ciphertext_n, extra_n);
+ dprintf("> iv=%d icv=%d\n", iv_n, icv_n);
+ skb_print_bits(skb, 0, skb->len);
+
+ // Add headroom for esp header and iv, tailroom for the ciphertext
+ // and icv.
+ head_n = ESP_HDR_N + iv_n;
+ tail_n = extra_n + icv_n;
+ err = skb_make_room(&skb, skb, head_n, tail_n);
+ if(err) goto exit;
+ dprintf("> skb=%p\n", skb);
+ // Move the headers up to make space for the esp header. We can
+ // use memmove() since all this data fits in the skb head.
+ // todo: Can't assume this anymore?
+ dprintf("> header push...\n");
+ __skb_push(skb, head_n);
+ if(0 && skb->mac.raw){
+ dprintf("> skb->mac=%p\n", skb->mac.raw);
+ dprintf("> ETH header pull...\n");
+ memmove(skb->data, skb->mac.raw, ETH_HLEN);
+ skb->mac.raw = skb->data;
+ __skb_pull(skb, ETH_HLEN);
+ }
+ dprintf("> IP header pull...\n");
+ memmove(skb->data, skb->nh.raw, ip_n);
+ skb->nh.raw = skb->data;
+ __skb_pull(skb, ip_n);
+ esph = (void*)skb->data;
+ // Add spi and sequence number.
+ esph->spi = sa->ident.spi;
+ esph->seq = htonl(++sa->replay.send_seq);
+ // Insert the padding bytes: extra bytes less the pad fields
+ // themselves.
+ dprintf("> esp_sa_pad ...\n");
+ esp_sa_pad(skb, icv_n, extra_n, skb->nh.iph->protocol);
+ if(sa->security & SA_CONF){
+ dprintf("> esp_sa_encrypt...\n");
+ err = esp_sa_encrypt(esp, esph, skb, head_n, iv_n, ciphertext_n);
+ if(err) goto exit;
+ }
+ if(icv_n){
+ dprintf("> esp_sa_digest...\n");
+ err = esp_sa_digest(esp, skb, head_n + ciphertext_n, icv_n);
+ if(err) goto exit;
+ }
+ dprintf("> IP header push...\n");
+ __skb_push(skb, ip_n);
+ if(0 && skb->mac.raw){
+ dprintf("> ETH header push...\n");
+ __skb_push(skb, ETH_HLEN);
+ }
+ // Fix ip header. Adjust length field, set protocol, zero
+ // checksum.
+ {
+ // Total packet length (bytes).
+ int tot_len = ntohs(skb->nh.iph->tot_len);
+ tot_len += head_n;
+ tot_len += tail_n;
+ skb->nh.iph->protocol = IPPROTO_ESP;
+ skb->nh.iph->tot_len = htons(tot_len);
+ skb->nh.iph->check = 0;
+ }
+ err = Tunnel_send(tunnel, skb);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Release an skb context.
+ * Drops the refcount on the SA.
+ *
+ * @param context to free
+ */
+static void esp_context_free_fn(SkbContext *context){
+ SAState *sa;
+ if(!context) return;
+ sa = context->data;
+ if(!sa) return;
+ context->data = NULL;
+ SAState_decref(sa);
+}
+
+/** Receive a packet via an ESP SA.
+ * Does ESP receive processing (check icv, decrypt), strips
+ * ESP header and re-receives.
+ *
+ * @param sa SA
+ * @param skb packet
+ * @return 0 on success, negative error code otherwise
+ */
+static int esp_sa_recv(SAState *sa, struct sk_buff *skb){
+ int err = -EINVAL;
+ int mine = 0;
+ int vnet = 0; //todo: fixme - need to record skb vnet somewhere
+ ESPState *esp;
+ ESPHdr *esph;
+ ESPPadding *pad;
+ int block_n; // Cipher blocksize.
+ int icv_n; // Size of integrity check value (icv).
+ int iv_n; // Size of initialization vector (iv).
+ int text_n; // Size of text (ciphertext or plaintext).
+ int head_n; // Size of esp header and iv.
+
+ dprintf("> skb=%p\n", skb);
+ // Assumes skb->data points at esp hdr.
+ esph = (void*)skb->data;
+ esp = sa->data;
+ block_n = crypto_tfm_alg_blocksize(esp->cipher.tfm);
+ icv_n = esp->digest.icv_n;
+ iv_n = esp->cipher.iv_n;
+ head_n = ESP_HDR_N + iv_n;
+ text_n = skb->len - head_n - icv_n;
+ if(text_n < ESP_PAD_N || !multipleof(text_n, block_n)){
+ wprintf("> Invalid size: text_n=%d tfm:block_n=%d esp:block_n=%d\n",
+ text_n, block_n, esp->cipher.block_n);
+ goto exit;
+ }
+ if(icv_n){
+ err = esp_check_icv(sa, esp, esph, skb);
+ if(err) goto exit;
+ }
+ mine = 1;
+ if(sa->security & SA_CONF){
+ err = esp_sa_decrypt(esp, esph, skb, head_n, iv_n, text_n);
+ if(err) goto exit;
+ }
+ // Strip esp header by moving the other headers down.
+ //todo Maybe not safe to do this anymore.
+ memmove(skb->mac.raw + head_n, skb->mac.raw, (skb->data - skb->mac.raw));
+ skb->mac.raw += head_n;
+ skb->nh.raw += head_n;
+ // Move skb->data back to ethernet header.
+ // Do in 2 moves to ensure offsets are +ve,
+ // since args to skb_pull/skb_push are unsigned.
+ __skb_pull(skb, head_n);
+ __skb_push(skb, skb->data - skb->mac.raw);
+ // After this esph is invalid.
+ esph = NULL;
+ // Trim padding, restore protocol in IP header.
+ pad = skb_trim_tail(skb, ESP_PAD_N);
+ text_n -= ESP_PAD_N;
+ if((pad->pad_n > 255) | (pad->pad_n > text_n)){
+ wprintf("> Invalid padding: pad_n=%d text_n=%d\n", pad->pad_n, text_n);
+ goto exit;
+ }
+ skb_trim_tail(skb, pad->pad_n);
+ skb->nh.iph->protocol = pad->protocol;
+ err = skb_push_context(skb, vnet, sa->ident.addr, IPPROTO_ESP,
+ sa, esp_context_free_fn);
+ if(err) goto exit;
+ // Increase sa refcount now the skb context refers to it.
+ SAState_incref(sa);
+ err = netif_rx(skb);
+ exit:
+ if(mine) err = 1;
+ dprintf("< skb=%p err=%d\n", skb, err);
+ return err;
+}
+
+/** Estimate the packet size for some data using ESP processing.
+ *
+ * @param sa ESP SA
+ * @param data_n data size
+ * @return size after ESP processing
+ */
+static u32 esp_sa_size(SAState *sa, int data_n){
+ // Even in transport mode have to round up to blocksize.
+ // Have to add some padding for alignment even if pad_n is zero.
+ ESPState *esp = sa->data;
+
+ data_n = roundup(data_n + ESP_PAD_N, esp->cipher.block_n);
+ if(esp->cipher.pad_n > 0){
+ data_n = roundup(data_n, esp->cipher.pad_n);
+ }
+ data_n += esp->digest.icv_n;
+ //data_n += esp->cipher.iv_n;
+ data_n += ESP_HDR_N;
+ return data_n;
+}
+
+/** Compute an icv using HMAC digest.
+ *
+ * @param esp ESP state
+ * @param skb packet to digest
+ * @param offset offset to start at
+ * @param len number of bytes to digest
+ * @param icv return parameter for ICV
+ * @return 0 on success, negative error code otherwise
+ */
+static inline void esp_hmac_digest(ESPState *esp, struct sk_buff *skb,
+ int offset, int len, u8 *icv){
+ int err = 0;
+ struct crypto_tfm *digest = esp->digest.tfm;
+ char *icv_tmp = esp->digest.icv_tmp;
+ int sg_n = skb_shinfo(skb)->nr_frags + 1;
+ struct scatterlist sg[sg_n];
+
+ dprintf("> offset=%d len=%d\n", offset, len);
+ memset(icv, 0, esp->digest.icv_n);
+ if(DEBUG_ICV){
+ dprintf("> key len=%d\n", esp->digest.key_n);
+ printk("\nkey=");
+ buf_print(esp->digest.key,esp->digest.key_n);
+ }
+ crypto_hmac_init(digest, esp->digest.key, &esp->digest.key_n);
+ err = skb_scatterlist(skb, sg, &sg_n, offset, len);
+ crypto_hmac_update(digest, sg, sg_n);
+ crypto_hmac_final(digest, esp->digest.key, &esp->digest.key_n, icv_tmp);
+ if(DEBUG_ICV){
+ dprintf("> digest len=%d ", esp->digest.icv_n);
+ printk("\nval=");
+ buf_print(icv_tmp, esp->digest.icv_n);
+ }
+ memcpy(icv, icv_tmp, esp->digest.icv_n);
+ dprintf("<\n");
+}
+
+/** Finish up an esp state.
+ * Releases the digest, cipher, iv and frees the state.
+ *
+ * @parma esp state
+ */
+static void esp_fini(ESPState *esp){
+ if(!esp) return;
+ if(esp->digest.tfm){
+ crypto_free_tfm(esp->digest.tfm);
+ esp->digest.tfm = NULL;
+ }
+ if(esp->digest.icv_tmp){
+ kfree(esp->digest.icv_tmp);
+ esp->digest.icv_tmp = NULL;
+ }
+ if(esp->cipher.tfm){
+ crypto_free_tfm(esp->cipher.tfm);
+ esp->cipher.tfm = NULL;
+ }
+ if(esp->cipher.iv){
+ kfree(esp->cipher.iv);
+ esp->cipher.iv = NULL;
+ }
+ kfree(esp);
+}
+
+/** Release an ESP SA.
+ *
+ * @param sa ESO SA
+ */
+static void esp_sa_fini(SAState *sa){
+ ESPState *esp;
+ if(!sa) return;
+ esp = sa->data;
+ if(!esp) return;
+ esp_fini(esp);
+ sa->data = NULL;
+}
+
+/** Initialize the cipher for an ESP SA.
+ *
+ * @param sa ESP SA
+ * @param esp ESP state
+ * @return 0 on success, negative error code otherwise
+ */
+static int esp_cipher_init(SAState *sa, ESPState *esp){
+ int err = 0;
+ SAAlgorithm *algo = NULL;
+ int cipher_mode = CRYPTO_TFM_MODE_CBC;
+
+ dprintf("> sa=%p esp=%p\n", sa, esp);
+ dprintf("> cipher=%s\n", sa->cipher.name);
+ algo = sa_cipher_by_name(sa->cipher.name);
+ if(!algo){
+ wprintf("> Cipher unavailable: %s\n", sa->cipher.name);
+ err = -EINVAL;
+ goto exit;
+ }
+ esp->cipher.key_n = roundup(sa->cipher.bits, 8);
+ // If cipher is null must use ECB because CBC algo does not support blocksize 1.
+ if(strcmp(sa->cipher.name, "cipher_null")){
+ cipher_mode = CRYPTO_TFM_MODE_ECB;
+ }
+ esp->cipher.tfm = crypto_alloc_tfm(sa->cipher.name, cipher_mode);
+ if(!esp->cipher.tfm){
+ err = -ENOMEM;
+ goto exit;
+ }
+ esp->cipher.block_n = roundup(crypto_tfm_alg_blocksize(esp->cipher.tfm), 4);
+ esp->cipher.iv_n = crypto_tfm_alg_ivsize(esp->cipher.tfm);
+ esp->cipher.pad_n = 0;
+ if(esp->cipher.iv_n){
+ esp->cipher.iv = kmalloc(esp->cipher.iv_n, GFP_KERNEL);
+ get_random_bytes(esp->cipher.iv, esp->cipher.iv_n);
+ }
+ crypto_cipher_setkey(esp->cipher.tfm, esp->cipher.key, esp->cipher.key_n);
+ err = 0;
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Initialize the digest for an ESP SA.
+ *
+ * @param sa ESP SA
+ * @param esp ESP state
+ * @return 0 on success, negative error code otherwise
+ */
+static int esp_digest_init(SAState *sa, ESPState *esp){
+ int err = 0;
+ SAAlgorithm *algo = NULL;
+
+ dprintf(">\n");
+ esp->digest.key = sa->digest.key;
+ esp->digest.key_n = bits_to_bytes(roundup(sa->digest.bits, 8));
+ esp->digest.tfm = crypto_alloc_tfm(sa->digest.name, 0);
+ if(!esp->digest.tfm){
+ err = -ENOMEM;
+ goto exit;
+ }
+ algo = sa_digest_by_name(sa->digest.name);
+ if(!algo){
+ wprintf("> Digest unavailable: %s\n", sa->digest.name);
+ err = -EINVAL;
+ goto exit;
+ }
+ esp->digest.icv = esp_hmac_digest;
+ esp->digest.icv_full_n = bits_to_bytes(algo->info.digest.icv_fullbits);
+ esp->digest.icv_n = bits_to_bytes(algo->info.digest.icv_truncbits);
+
+ if(esp->digest.icv_full_n != crypto_tfm_alg_digestsize(esp->digest.tfm)){
+ err = -EINVAL;
+ wprintf("> digest %s, size %u != %hu\n",
+ sa->digest.name,
+ crypto_tfm_alg_digestsize(esp->digest.tfm),
+ esp->digest.icv_full_n);
+ goto exit;
+ }
+
+ esp->digest.icv_tmp = kmalloc(esp->digest.icv_full_n, GFP_KERNEL);
+ if(!esp->digest.icv_tmp){
+ err = -ENOMEM;
+ goto exit;
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Initialize an ESP SA.
+ *
+ * @param sa ESP SA
+ * @param args arguments
+ * @return 0 on success, negative error code otherwise
+ */
+static int esp_sa_init(SAState *sa, void *args){
+ int err = 0;
+ ESPState *esp = NULL;
+
+ dprintf("> sa=%p\n", sa);
+ esp = kmalloc(sizeof(*esp), GFP_KERNEL);
+ if(!esp){
+ err = -ENOMEM;
+ goto exit;
+ }
+ *esp = (ESPState){};
+ err = esp_cipher_init(sa, esp);
+ if(err) goto exit;
+ err = esp_digest_init(sa, esp);
+ if(err) goto exit;
+ sa->data = esp;
+ exit:
+ if(err){
+ if(esp) esp_fini(esp);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** SA type for ESP.
+ */
+static SAType esp_sa_type = {
+ .name = "ESP",
+ .protocol = IPPROTO_ESP,
+ .init = esp_sa_init,
+ .fini = esp_sa_fini,
+ .size = esp_sa_size,
+ .recv = esp_sa_recv,
+ .send = esp_sa_send
+};
+
+/** Get the ESP header from a packet.
+ *
+ * @param skb packet
+ * @param esph return parameter for header
+ * @return 0 on success, negative error code otherwise
+ */
+static int esp_skb_header(struct sk_buff *skb, ESPHdr **esph){
+ int err = 0;
+ if(skb->len < ESP_HDR_N){
+ err = -EINVAL;
+ goto exit;
+ }
+ *esph = (ESPHdr*)skb->data;
+ exit:
+ return err;
+}
+
+/** Handle an incoming skb with ESP protocol.
+ *
+ * Lookup spi, if state found hand to the state.
+ * If no state, check spi, if ok, create state and pass to it.
+ * If spi not ok, drop.
+ *
+ * @param skb packet
+ * @return 0 on sucess, negative error code otherwise
+ */
+static int esp_protocol_recv(struct sk_buff *skb){
+ int err = 0;
+ const int eth_n = ETH_HLEN;
+ int ip_n;
+ ESPHdr *esph = NULL;
+ SAState *sa = NULL;
+ u32 addr;
+
+ dprintf(">\n");
+ dprintf("> recv skb=\n"); skb_print_bits(skb, 0, skb->len);
+ ip_n = (skb->nh.iph->ihl << 2);
+ if(skb->data == skb->mac.raw){
+ // skb->data points at ethernet header.
+ if (!pskb_may_pull(skb, eth_n + ip_n)){
+ wprintf("> Malformed skb\n");
+ err = -EINVAL;
+ goto exit;
+ }
+ skb_pull(skb, eth_n + ip_n);
+ }
+ addr = skb->nh.iph->daddr;
+ err = esp_skb_header(skb, &esph);
+ if(err) goto exit;
+ dprintf("> spi=%08x protocol=%d addr=" IPFMT "\n",
+ esph->spi, IPPROTO_ESP, NIPQUAD(addr));
+ sa = sa_table_lookup_spi(esph->spi, IPPROTO_ESP, addr);
+ if(!sa){
+ err = vnet_sa_create(esph->spi, IPPROTO_ESP, addr, &sa);
+ if(err) goto exit;
+ }
+ err = SAState_recv(sa, skb);
+ exit:
+ if(sa) SAState_decref(sa);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Handle an ICMP error related to ESP.
+ *
+ * @param skb ICMP error packet
+ * @param info
+ */
+static void esp_protocol_icmp_err(struct sk_buff *skb, u32 info){
+ struct iphdr *iph = (struct iphdr*)skb->data;
+ ESPHdr *esph;
+ SAState *sa;
+
+ dprintf("> ICMP error type=%d code=%d\n",
+ skb->h.icmph->type, skb->h.icmph->code);
+ if(skb->h.icmph->type != ICMP_DEST_UNREACH ||
+ skb->h.icmph->code != ICMP_FRAG_NEEDED){
+ return;
+ }
+
+ //todo: need to check skb has enough len to do this.
+ esph = (ESPHdr*)(skb->data + (iph->ihl << 2));
+ sa = sa_table_lookup_spi(esph->spi, IPPROTO_ESP, iph->daddr);
+ if(!sa) return;
+ wprintf("> ICMP unreachable on SA ESP spi=%08x addr=" IPFMT "\n",
+ ntohl(esph->spi), NIPQUAD(iph->daddr));
+ SAState_decref(sa);
+}
+
+//============================================================================
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+// Code for 2.6 kernel.
+
+/** Protocol handler for ESP.
+ */
+static struct net_protocol esp_protocol = {
+ .handler = esp_protocol_recv,
+ .err_handler = esp_protocol_icmp_err
+};
+
+static int esp_protocol_add(void){
+ return inet_add_protocol(&esp_protocol, IPPROTO_ESP);
+}
+
+static int esp_protocol_del(void){
+ return inet_del_protocol(&esp_protocol, IPPROTO_ESP);
+}
+
+//============================================================================
+#else
+//============================================================================
+// Code for 2.4 kernel.
+
+/** Protocol handler for ESP.
+ */
+static struct inet_protocol esp_protocol = {
+ .name = "ESP",
+ .protocol = IPPROTO_ESP,
+ .handler = esp_protocol_recv,
+ .err_handler = esp_protocol_icmp_err
+};
+
+static int esp_protocol_add(void){
+ inet_add_protocol(&esp_protocol);
+ return 0;
+}
+
+static int esp_protocol_del(void){
+ return inet_del_protocol(&esp_protocol);
+}
+
+#endif
+//============================================================================
+
+
+/** Initialize the ESP module.
+ * Registers the ESP protocol and SA type.
+ *
+ * @return 0 on success, negative error code otherwise
+ */
+int __init esp_module_init(void){
+ int err = 0;
+ dprintf(">\n");
+ err = SAType_add(&esp_sa_type);
+ if(err < 0){
+ eprintf("> Error adding esp sa type\n");
+ goto exit;
+ }
+ esp_protocol_add();
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Finalize the ESP module.
+ * Deregisters the ESP protocol and SA type.
+ */
+void __exit esp_module_exit(void){
+ if(esp_protocol_del() < 0){
+ eprintf("> Error removing esp protocol\n");
+ }
+ if(SAType_del(&esp_sa_type) < 0){
+ eprintf("> Error removing esp sa type\n");
+ }
+}
+
diff --git a/tools/vnet/vnet-module/esp.h b/tools/vnet/vnet-module/esp.h
new file mode 100644
index 0000000000..57d21a9ce7
--- /dev/null
+++ b/tools/vnet/vnet-module/esp.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __VNET_ESP_H__
+#define __VNET_ESP_H__
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/crypto.h>
+
+/** Header used by IPSEC ESP (Encapsulated Security Payload). */
+typedef struct ESPHdr {
+ /** The spi (security parameters index). */
+ u32 spi;
+ /** Sequence number. */
+ u32 seq;
+ /* Variable length data (depends on crypto suite).
+ Mind the 64 bit alignment! */
+ u8 data[0];
+} ESPHdr;
+
+/** Padding trailer used by IPSEC ESP.
+ * Follows the padding itself with the padding length and the
+ * protocol being encapsulated.
+ */
+typedef struct ESPPadding {
+ u8 pad_n;
+ u8 protocol;
+} ESPPadding;
+
+/** Size of the esp header (spi and seq). */
+static const int ESP_HDR_N = sizeof(ESPHdr);
+
+/** Size of the esp pad and next protocol field. */
+static const int ESP_PAD_N = sizeof(ESPPadding);
+
+enum {
+ SASTATE_VOID,
+ SASTATE_ACQUIRE,
+ SASTATE_VALID,
+ SASTATE_ERROR,
+ SASTATE_EXPIRED,
+ SASTATE_DEAD,
+};
+
+struct ESPState;
+
+/** A cipher instance. */
+typedef struct ESPCipher {
+ /** Cipher key. */
+ u8 *key;
+ /** Key size (bytes). */
+ int key_n;
+ /** Initialization vector (IV). */
+ u8 *iv;
+ /** IV size (bytes). */
+ int iv_n;
+ /** Block size for padding (bytes). */
+ int pad_n;
+ /** Cipher block size (bytes). */
+ int block_n;
+ /** Cipher crypto transform. */
+ struct crypto_tfm *tfm;
+} ESPCipher;
+
+/** A digest instance. */
+typedef struct ESPDigest {
+ /** Digest key. */
+ u8 *key;
+ /** Key size (bytes) */
+ int key_n;
+ /** ICV size used (bytes). */
+ u8 icv_n;
+ /** Full ICV size when computed (bytes). */
+ u8 icv_full_n;
+ /** Working storage for computing ICV. */
+ u8 *icv_tmp;
+ /** Function used to compute ICV (e.g. HMAC). */
+ void (*icv)(struct ESPState *esp,
+ struct sk_buff *skb,
+ int offset,
+ int len,
+ u8 *icv);
+ /** Digest crypto transform (e.g. SHA). */
+ struct crypto_tfm *tfm;
+} ESPDigest;
+
+typedef struct ESPState {
+ struct ESPCipher cipher;
+ struct ESPDigest digest;
+} ESPState;
+
+extern int esp_module_init(void);
+extern void esp_module_exit(void);
+
+#endif /* !__VNET_ESP_H__ */
diff --git a/tools/vnet/vnet-module/etherip.c b/tools/vnet/vnet-module/etherip.c
new file mode 100644
index 0000000000..05486ed5a2
--- /dev/null
+++ b/tools/vnet/vnet-module/etherip.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <linux/version.h>
+
+#include <linux/skbuff.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/icmp.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/checksum.h>
+
+#include <etherip.h>
+#include <tunnel.h>
+#include <vnet.h>
+#include <varp.h>
+#include <if_varp.h>
+#include <skb_util.h>
+
+#define MODULE_NAME "VNET"
+//#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** @file Etherip implementation.
+ * The etherip protocol is used to transport Ethernet frames in IP packets.
+ */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#define MAC_ETH(_skb) ((struct ethhdr *)(_skb)->mac.raw)
+#else
+#define MAC_ETH(_skb) ((_skb)->mac.ethernet)
+#endif
+
+/** Get the vnet label from an etherip header.
+ *
+ * @param hdr header
+ * @return vnet (in host order)
+ */
+int etheriphdr_get_vnet(struct etheriphdr *hdr){
+#ifdef CONFIG_ETHERIP_EXT
+ return ntohl(hdr->vnet);
+#else
+ return hdr->reserved;
+#endif
+}
+
+/** Set the vnet label in an etherip header.
+ * Also sets the etherip version.
+ *
+ * @param hdr header
+ * @param vnet vnet label (in host order)
+ */
+void etheriphdr_set_vnet(struct etheriphdr *hdr, int vnet){
+#ifdef CONFIG_ETHERIP_EXT
+ hdr->version = 4;
+ hdr->vnet = htonl(vnet);
+#else
+ hdr->version = 3;
+ hdr->reserved = vnet & 0x0fff;
+#endif
+}
+
+/** Open an etherip tunnel.
+ *
+ * @param tunnel to open
+ * @return 0 on success, error code otherwise
+ */
+static int etherip_tunnel_open(Tunnel *tunnel){
+ return 0;
+}
+
+/** Close an etherip tunnel.
+ *
+ * @param tunnel to close
+ */
+static void etherip_tunnel_close(Tunnel *tunnel){
+}
+
+
+/** Send a packet via an etherip tunnel.
+ * Adds etherip header, new ip header, new ethernet header around
+ * ethernet frame.
+ *
+ * @param tunnel tunnel
+ * @param skb packet
+ * @return 0 on success, error code otherwise
+ */
+static int etherip_tunnel_send(Tunnel *tunnel, struct sk_buff *skb){
+ int err = 0;
+ const int etherip_n = sizeof(struct etheriphdr);
+ const int ip_n = sizeof(struct iphdr);
+ const int eth_n = ETH_HLEN;
+ int head_n = 0;
+ int vnet = tunnel->key.vnet;
+ struct etheriphdr *etheriph;
+ struct ethhdr *ethh;
+ u32 saddr = 0;
+
+ dprintf("> skb=%p vnet=%d\n", skb, vnet);
+ head_n = etherip_n + ip_n + eth_n;
+ err = skb_make_room(&skb, skb, head_n, 0);
+ if(err) goto exit;
+
+ //err = vnet_get_device_address(skb->dev, &saddr);
+ //if(err) goto exit;
+
+ // The original ethernet header.
+ ethh = MAC_ETH(skb);
+ //print_skb_data(__FUNCTION__, 0, skb, skb->mac.raw, skb->len);
+ // Null the pointer as we are pushing a new IP header.
+ skb->mac.raw = NULL;
+
+ // Setup the etherip header.
+ //dprintf("> push etherip header...\n");
+ etheriph = (struct etheriphdr *)skb_push(skb, etherip_n);
+ etheriphdr_set_vnet(etheriph, vnet);
+
+ // Setup the IP header.
+ //dprintf("> push IP header...\n");
+ skb->nh.raw = skb_push(skb, ip_n);
+ skb->nh.iph->version = 4; // Standard version.
+ skb->nh.iph->ihl = ip_n / 4; // IP header length (32-bit words).
+ skb->nh.iph->tos = 0; // No special type-of-service.
+ skb->nh.iph->tot_len = htons(skb->len); // Total packet length (bytes).
+ skb->nh.iph->id = 0; // No flow id (since no frags).
+ skb->nh.iph->frag_off = htons(IP_DF); // Don't fragment - can't handle frags.
+ skb->nh.iph->ttl = 64; // Linux default time-to-live.
+ skb->nh.iph->protocol = IPPROTO_ETHERIP; // IP protocol number.
+ skb->nh.iph->saddr = saddr; // Source address.
+ skb->nh.iph->daddr = tunnel->key.addr; // Destination address.
+ skb->nh.iph->check = 0;
+
+ // Ethernet header will be filled-in by device.
+ err = Tunnel_send(tunnel->base, skb);
+ skb = NULL;
+ exit:
+ if(err && skb) dev_kfree_skb(skb);
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Tunnel type for etherip.
+ */
+static TunnelType _etherip_tunnel_type = {
+ .name = "ETHERIP",
+ .open = etherip_tunnel_open,
+ .close = etherip_tunnel_close,
+ .send = etherip_tunnel_send
+};
+
+TunnelType *etherip_tunnel_type = &_etherip_tunnel_type;
+
+/* Defeat compiler warnings about unused functions. */
+static void print_str(char *s, int n) __attribute__((unused));
+
+static void print_str(char *s, int n) {
+ int i;
+
+ for(i=0; i<n; s++, i++){
+ if(i && i % 40 == 0) printk("\n");
+ if(('a'<= *s && *s <= 'z') ||
+ ('A'<= *s && *s <= 'Z') ||
+ ('0'<= *s && *s <= '9')){
+ printk("%c", *s);
+ } else {
+ printk("<%x>", (unsigned)(0xff & *s));
+ }
+ }
+ printk("\n");
+}
+
+/** Do etherip receive processing.
+ * Strips etherip header to extract the ethernet frame, sets
+ * the vnet from the header and re-receives the frame.
+ *
+ * @param skb packet
+ * @return 0 on success, error code otherwise
+ */
+static int etherip_protocol_recv(struct sk_buff *skb){
+ int err = 0;
+ int mine = 0;
+ const int eth_n = ETH_HLEN;
+ int ip_n;
+ const int etherip_n = sizeof(struct etheriphdr);
+ struct etheriphdr *etheriph;
+ struct ethhdr *ethhdr;
+ Vnet *vinfo = NULL;
+ u32 vnet;
+
+ ethhdr = MAC_ETH(skb);
+ if(MULTICAST(skb->nh.iph->daddr) &&
+ (skb->nh.iph->daddr != varp_mcast_addr)){
+ // Ignore multicast packets not addressed to us.
+ dprintf("> dst=%u.%u.%u.%u varp_mcast_addr=%u.%u.%u.%u\n",
+ NIPQUAD(skb->nh.iph->daddr),
+ NIPQUAD(varp_mcast_addr));
+ goto exit;
+ }
+ ip_n = (skb->nh.iph->ihl << 2);
+ if(skb->data == skb->mac.raw){
+ // skb->data points at ethernet header.
+ //dprintf("> len=%d\n", skb->len);
+ if (!pskb_may_pull(skb, eth_n + ip_n)){
+ wprintf("> Malformed skb\n");
+ err = -EINVAL;
+ goto exit;
+ }
+ skb_pull(skb, eth_n + ip_n);
+ }
+ // Assume skb->data points at etherip header.
+ etheriph = (void*)skb->data;
+ if(!pskb_may_pull(skb, etherip_n)){
+ wprintf("> Malformed skb\n");
+ err = -EINVAL;
+ goto exit;
+ }
+ vnet = etheriphdr_get_vnet(etheriph);
+ dprintf("> Rcvd skb=%p vnet=%d\n", skb, vnet);
+ // If vnet is secure, context must include IPSEC ESP.
+ err = vnet_check_context(vnet, SKB_CONTEXT(skb), &vinfo);
+ Vnet_decref(vinfo);
+ if(err){
+ wprintf("> Failed security check\n");
+ goto exit;
+ }
+ mine = 1;
+ // Point at the headers in the contained ethernet frame.
+ skb->mac.raw = skb_pull(skb, etherip_n);
+
+ // Know source ip, vnet, vmac, so could update varp cache.
+ // But if traffic comes to us over a vnetd tunnel this points the coa
+ // at the vnetd rather than the endpoint. So don't do it.
+ //varp_update(htonl(vnet), MAC_ETH(skb)->h_source, skb->nh.iph->saddr);
+
+ // Assuming a standard Ethernet frame.
+ skb->nh.raw = skb_pull(skb, ETH_HLEN);
+
+#ifdef CONFIG_NETFILTER
+#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+ // This stops our new pkt header being clobbered by a subsequent
+ // call to nf_bridge_maybe_copy_header. Just replicate the
+ // corresponding nf_bridge_save_header.
+ if(skb->nf_bridge){
+ int header_size = 16;
+ if(MAC_ETH(skb)->h_proto == __constant_htons(ETH_P_8021Q)) {
+ header_size = 18;
+ }
+ memcpy(skb->nf_bridge->data, skb->data - header_size, header_size);
+ }
+#endif
+#endif
+
+ if(1){
+ struct ethhdr *eth = MAC_ETH(skb);
+ // Devices use eth_type_trans() to set skb->pkt_type and skb->protocol.
+ // Set them from contained ethhdr, or leave as received?
+ // 'Ware use of hard_header_len in eth_type_trans().
+
+ //skb->protocol = htons(ETH_P_IP);
+
+ if(ntohs(eth->h_proto) >= 1536){
+ skb->protocol = eth->h_proto;
+ } else {
+ skb->protocol = htons(ETH_P_802_2);
+ }
+
+ if(mac_is_multicast(eth->h_dest)){
+ if(mac_is_broadcast(eth->h_dest)){
+ skb->pkt_type = PACKET_BROADCAST;
+ } else {
+ skb->pkt_type = PACKET_MULTICAST;
+ }
+ } else {
+ skb->pkt_type = PACKET_HOST;
+ }
+
+ memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
+ if (skb->ip_summed == CHECKSUM_HW){
+ skb->ip_summed = CHECKSUM_NONE;
+ //skb->csum = csum_sub(skb->csum,
+ // csum_partial(skb->mac.raw, skb->nh.raw - skb->mac.raw, 0));
+ }
+ dst_release(skb->dst);
+ skb->dst = NULL;
+#ifdef CONFIG_NETFILTER
+ nf_conntrack_put(skb->nfct);
+ skb->nfct = NULL;
+#ifdef CONFIG_NETFILTER_DEBUG
+ skb->nf_debug = 0;
+#endif
+#endif
+ }
+
+ //print_skb_data(__FUNCTION__, 0, skb, skb->mac.raw, skb->len + ETH_HLEN);
+
+ err = vnet_skb_recv(skb, vnet, (Vmac*)MAC_ETH(skb)->h_dest);
+ exit:
+ if(mine) err = 1;
+ dprintf("< skb=%p err=%d\n", skb, err);
+ return err;
+}
+
+/** Handle an ICMP error related to etherip.
+ *
+ * @param skb ICMP error packet
+ * @param info
+ */
+static void etherip_protocol_icmp_err(struct sk_buff *skb, u32 info){
+ struct iphdr *iph = (struct iphdr*)skb->data;
+
+ wprintf("> ICMP error type=%d code=%d addr=" IPFMT "\n",
+ skb->h.icmph->type, skb->h.icmph->code, NIPQUAD(iph->daddr));
+
+ if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
+ skb->h.icmph->code != ICMP_FRAG_NEEDED){
+ return;
+ }
+ wprintf("> MTU too big addr= " IPFMT "\n", NIPQUAD(iph->daddr));
+}
+
+//============================================================================
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+// Code for 2.6 kernel.
+
+/** Etherip protocol. */
+static struct net_protocol etherip_protocol = {
+ .handler = etherip_protocol_recv,
+ .err_handler = etherip_protocol_icmp_err,
+};
+
+static int etherip_protocol_add(void){
+ return inet_add_protocol(&etherip_protocol, IPPROTO_ETHERIP);
+}
+
+static int etherip_protocol_del(void){
+ return inet_del_protocol(&etherip_protocol, IPPROTO_ETHERIP);
+}
+
+//============================================================================
+#else
+//============================================================================
+// Code for 2.4 kernel.
+
+/** Etherip protocol. */
+static struct inet_protocol etherip_protocol = {
+ .name = "ETHERIP",
+ .protocol = IPPROTO_ETHERIP,
+ .handler = etherip_protocol_recv,
+ .err_handler = etherip_protocol_icmp_err,
+};
+
+static int etherip_protocol_add(void){
+ inet_add_protocol(&etherip_protocol);
+ return 0;
+}
+
+static int etherip_protocol_del(void){
+ return inet_del_protocol(&etherip_protocol);
+}
+
+#endif
+//============================================================================
+
+
+/** Initialize the etherip module.
+ * Registers the etherip protocol.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int __init etherip_module_init(void) {
+ int err = 0;
+ etherip_protocol_add();
+ return err;
+}
+
+/** Finalize the etherip module.
+ * Deregisters the etherip protocol.
+ */
+void __exit etherip_module_exit(void) {
+ if(etherip_protocol_del() < 0){
+ printk(KERN_INFO "%s: can't remove etherip protocol\n", __FUNCTION__);
+ }
+}
diff --git a/tools/vnet/vnet-module/etherip.h b/tools/vnet/vnet-module/etherip.h
new file mode 100644
index 0000000000..e8c23c2c2f
--- /dev/null
+++ b/tools/vnet/vnet-module/etherip.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _VNET_ETHERIP_H_
+#define _VNET_ETHERIP_H_
+
+#include "if_etherip.h"
+
+extern int etherip_module_init(void);
+extern void etherip_module_exit(void);
+
+#endif
diff --git a/tools/vnet/vnet-module/if_etherip.h b/tools/vnet/vnet-module/if_etherip.h
new file mode 100644
index 0000000000..272c345d78
--- /dev/null
+++ b/tools/vnet/vnet-module/if_etherip.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _VNET_IF_ETHERIP_H_
+#define _VNET_IF_ETHERIP_H_
+/*----------------------------------------------------------------------------*/
+#ifdef CONFIG_ETHERIP_EXT
+struct etheriphdr {
+ __u8 version;
+ __u32 vnet;
+} __attribute__ ((packed));
+
+/*----------------------------------------------------------------------------*/
+#else
+struct etheriphdr
+{
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u16 reserved:12,
+ version:4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+ __u16 version:4,
+ reserved:12;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+
+};
+#endif
+
+#ifndef IPPROTO_ETHERIP
+#define IPPROTO_ETHERIP 97
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+#endif /* ! _VNET_IF_ETHERIP_H_ */
diff --git a/tools/vnet/vnet-module/if_varp.h b/tools/vnet/vnet-module/if_varp.h
new file mode 100644
index 0000000000..c4b752ac49
--- /dev/null
+++ b/tools/vnet/vnet-module/if_varp.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _VNET_IF_VARP_H
+#define _VNET_IF_VARP_H
+
+typedef struct Vmac {
+ unsigned char mac[ETH_ALEN];
+} Vmac;
+
+enum {
+ VARP_ID = 1,
+ VARP_OP_REQUEST = 1,
+ VARP_OP_ANNOUNCE = 2,
+};
+
+typedef struct VnetMsgHdr {
+ uint16_t id;
+ uint16_t opcode;
+} __attribute__((packed)) VnetMsgHdr;
+
+typedef struct VarpHdr {
+ VnetMsgHdr vnetmsghdr;
+ uint32_t vnet;
+ Vmac vmac;
+ uint32_t addr;
+} __attribute__((packed)) VarpHdr;
+
+/** Default address for varp/vnet broadcasts: 224.10.0.1 */
+#define VARP_MCAST_ADDR 0xe00a0001
+
+/** UDP port to use for varp protocol. */
+#define VARP_PORT 1798
+
+
+
+#endif /* ! _VNET_IF_VARP_H */
diff --git a/tools/vnet/vnet-module/linux/pfkeyv2.h b/tools/vnet/vnet-module/linux/pfkeyv2.h
new file mode 100644
index 0000000000..cf3a2f1622
--- /dev/null
+++ b/tools/vnet/vnet-module/linux/pfkeyv2.h
@@ -0,0 +1,329 @@
+/* PF_KEY user interface, this is defined by rfc2367 so
+ * do not make arbitrary modifications or else this header
+ * file will not be compliant.
+ */
+
+#ifndef _LINUX_PFKEY2_H
+#define _LINUX_PFKEY2_H
+
+#include <linux/types.h>
+
+#define PF_KEY_V2 2
+#define PFKEYV2_REVISION 199806L
+
+struct sadb_msg {
+ uint8_t sadb_msg_version;
+ uint8_t sadb_msg_type;
+ uint8_t sadb_msg_errno;
+ uint8_t sadb_msg_satype;
+ uint16_t sadb_msg_len;
+ uint16_t sadb_msg_reserved;
+ uint32_t sadb_msg_seq;
+ uint32_t sadb_msg_pid;
+} __attribute__((packed));
+/* sizeof(struct sadb_msg) == 16 */
+
+struct sadb_ext {
+ uint16_t sadb_ext_len;
+ uint16_t sadb_ext_type;
+} __attribute__((packed));
+/* sizeof(struct sadb_ext) == 4 */
+
+struct sadb_sa {
+ uint16_t sadb_sa_len;
+ uint16_t sadb_sa_exttype;
+ uint32_t sadb_sa_spi;
+ uint8_t sadb_sa_replay;
+ uint8_t sadb_sa_state;
+ uint8_t sadb_sa_auth;
+ uint8_t sadb_sa_encrypt;
+ uint32_t sadb_sa_flags;
+} __attribute__((packed));
+/* sizeof(struct sadb_sa) == 16 */
+
+struct sadb_lifetime {
+ uint16_t sadb_lifetime_len;
+ uint16_t sadb_lifetime_exttype;
+ uint32_t sadb_lifetime_allocations;
+ uint64_t sadb_lifetime_bytes;
+ uint64_t sadb_lifetime_addtime;
+ uint64_t sadb_lifetime_usetime;
+} __attribute__((packed));
+/* sizeof(struct sadb_lifetime) == 32 */
+
+struct sadb_address {
+ uint16_t sadb_address_len;
+ uint16_t sadb_address_exttype;
+ uint8_t sadb_address_proto;
+ uint8_t sadb_address_prefixlen;
+ uint16_t sadb_address_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_address) == 8 */
+
+struct sadb_key {
+ uint16_t sadb_key_len;
+ uint16_t sadb_key_exttype;
+ uint16_t sadb_key_bits;
+ uint16_t sadb_key_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_key) == 8 */
+
+struct sadb_ident {
+ uint16_t sadb_ident_len;
+ uint16_t sadb_ident_exttype;
+ uint16_t sadb_ident_type;
+ uint16_t sadb_ident_reserved;
+ uint64_t sadb_ident_id;
+} __attribute__((packed));
+/* sizeof(struct sadb_ident) == 16 */
+
+struct sadb_sens {
+ uint16_t sadb_sens_len;
+ uint16_t sadb_sens_exttype;
+ uint32_t sadb_sens_dpd;
+ uint8_t sadb_sens_sens_level;
+ uint8_t sadb_sens_sens_len;
+ uint8_t sadb_sens_integ_level;
+ uint8_t sadb_sens_integ_len;
+ uint32_t sadb_sens_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_sens) == 16 */
+
+/* followed by:
+ uint64_t sadb_sens_bitmap[sens_len];
+ uint64_t sadb_integ_bitmap[integ_len]; */
+
+struct sadb_prop {
+ uint16_t sadb_prop_len;
+ uint16_t sadb_prop_exttype;
+ uint8_t sadb_prop_replay;
+ uint8_t sadb_prop_reserved[3];
+} __attribute__((packed));
+/* sizeof(struct sadb_prop) == 8 */
+
+/* followed by:
+ struct sadb_comb sadb_combs[(sadb_prop_len +
+ sizeof(uint64_t) - sizeof(struct sadb_prop)) /
+ sizeof(strut sadb_comb)]; */
+
+struct sadb_comb {
+ uint8_t sadb_comb_auth;
+ uint8_t sadb_comb_encrypt;
+ uint16_t sadb_comb_flags;
+ uint16_t sadb_comb_auth_minbits;
+ uint16_t sadb_comb_auth_maxbits;
+ uint16_t sadb_comb_encrypt_minbits;
+ uint16_t sadb_comb_encrypt_maxbits;
+ uint32_t sadb_comb_reserved;
+ uint32_t sadb_comb_soft_allocations;
+ uint32_t sadb_comb_hard_allocations;
+ uint64_t sadb_comb_soft_bytes;
+ uint64_t sadb_comb_hard_bytes;
+ uint64_t sadb_comb_soft_addtime;
+ uint64_t sadb_comb_hard_addtime;
+ uint64_t sadb_comb_soft_usetime;
+ uint64_t sadb_comb_hard_usetime;
+} __attribute__((packed));
+/* sizeof(struct sadb_comb) == 72 */
+
+struct sadb_supported {
+ uint16_t sadb_supported_len;
+ uint16_t sadb_supported_exttype;
+ uint32_t sadb_supported_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_supported) == 8 */
+
+/* followed by:
+ struct sadb_alg sadb_algs[(sadb_supported_len +
+ sizeof(uint64_t) - sizeof(struct sadb_supported)) /
+ sizeof(struct sadb_alg)]; */
+
+struct sadb_alg {
+ uint8_t sadb_alg_id;
+ uint8_t sadb_alg_ivlen;
+ uint16_t sadb_alg_minbits;
+ uint16_t sadb_alg_maxbits;
+ uint16_t sadb_alg_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_alg) == 8 */
+
+struct sadb_spirange {
+ uint16_t sadb_spirange_len;
+ uint16_t sadb_spirange_exttype;
+ uint32_t sadb_spirange_min;
+ uint32_t sadb_spirange_max;
+ uint32_t sadb_spirange_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_spirange) == 16 */
+
+struct sadb_x_kmprivate {
+ uint16_t sadb_x_kmprivate_len;
+ uint16_t sadb_x_kmprivate_exttype;
+ u_int32_t sadb_x_kmprivate_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_x_kmprivate) == 8 */
+
+struct sadb_x_sa2 {
+ uint16_t sadb_x_sa2_len;
+ uint16_t sadb_x_sa2_exttype;
+ uint8_t sadb_x_sa2_mode;
+ uint8_t sadb_x_sa2_reserved1;
+ uint16_t sadb_x_sa2_reserved2;
+ uint32_t sadb_x_sa2_sequence;
+ uint32_t sadb_x_sa2_reqid;
+} __attribute__((packed));
+/* sizeof(struct sadb_x_sa2) == 16 */
+
+struct sadb_x_policy {
+ uint16_t sadb_x_policy_len;
+ uint16_t sadb_x_policy_exttype;
+ uint16_t sadb_x_policy_type;
+ uint8_t sadb_x_policy_dir;
+ uint8_t sadb_x_policy_reserved;
+ uint32_t sadb_x_policy_id;
+ uint32_t sadb_x_policy_reserved2;
+} __attribute__((packed));
+/* sizeof(struct sadb_x_policy) == 16 */
+
+struct sadb_x_ipsecrequest {
+ uint16_t sadb_x_ipsecrequest_len;
+ uint16_t sadb_x_ipsecrequest_proto;
+ uint8_t sadb_x_ipsecrequest_mode;
+ uint8_t sadb_x_ipsecrequest_level;
+ uint16_t sadb_x_ipsecrequest_reqid;
+} __attribute__((packed));
+/* sizeof(struct sadb_x_ipsecrequest) == 16 */
+
+/* This defines the TYPE of Nat Traversal in use. Currently only one
+ * type of NAT-T is supported, draft-ietf-ipsec-udp-encaps-06
+ */
+struct sadb_x_nat_t_type {
+ uint16_t sadb_x_nat_t_type_len;
+ uint16_t sadb_x_nat_t_type_exttype;
+ uint8_t sadb_x_nat_t_type_type;
+ uint8_t sadb_x_nat_t_type_reserved[3];
+} __attribute__((packed));
+/* sizeof(struct sadb_x_nat_t_type) == 8 */
+
+/* Pass a NAT Traversal port (Source or Dest port) */
+struct sadb_x_nat_t_port {
+ uint16_t sadb_x_nat_t_port_len;
+ uint16_t sadb_x_nat_t_port_exttype;
+ uint16_t sadb_x_nat_t_port_port;
+ uint16_t sadb_x_nat_t_port_reserved;
+} __attribute__((packed));
+/* sizeof(struct sadb_x_nat_t_port) == 8 */
+
+/* Message types */
+#define SADB_RESERVED 0
+#define SADB_GETSPI 1
+#define SADB_UPDATE 2
+#define SADB_ADD 3
+#define SADB_DELETE 4
+#define SADB_GET 5
+#define SADB_ACQUIRE 6
+#define SADB_REGISTER 7
+#define SADB_EXPIRE 8
+#define SADB_FLUSH 9
+#define SADB_DUMP 10
+#define SADB_X_PROMISC 11
+#define SADB_X_PCHANGE 12
+#define SADB_X_SPDUPDATE 13
+#define SADB_X_SPDADD 14
+#define SADB_X_SPDDELETE 15
+#define SADB_X_SPDGET 16
+#define SADB_X_SPDACQUIRE 17
+#define SADB_X_SPDDUMP 18
+#define SADB_X_SPDFLUSH 19
+#define SADB_X_SPDSETIDX 20
+#define SADB_X_SPDEXPIRE 21
+#define SADB_X_SPDDELETE2 22
+#define SADB_X_NAT_T_NEW_MAPPING 23
+#define SADB_MAX 23
+
+/* Security Association flags */
+#define SADB_SAFLAGS_PFS 1
+
+/* Security Association states */
+#define SADB_SASTATE_LARVAL 0
+#define SADB_SASTATE_MATURE 1
+#define SADB_SASTATE_DYING 2
+#define SADB_SASTATE_DEAD 3
+#define SADB_SASTATE_MAX 3
+
+/* Security Association types */
+#define SADB_SATYPE_UNSPEC 0
+#define SADB_SATYPE_AH 2
+#define SADB_SATYPE_ESP 3
+#define SADB_SATYPE_RSVP 5
+#define SADB_SATYPE_OSPFV2 6
+#define SADB_SATYPE_RIPV2 7
+#define SADB_SATYPE_MIP 8
+#define SADB_X_SATYPE_IPCOMP 9
+#define SADB_SATYPE_MAX 9
+
+/* Authentication algorithms */
+#define SADB_AALG_NONE 0
+#define SADB_AALG_MD5HMAC 2
+#define SADB_AALG_SHA1HMAC 3
+#define SADB_X_AALG_SHA2_256HMAC 5
+#define SADB_X_AALG_SHA2_384HMAC 6
+#define SADB_X_AALG_SHA2_512HMAC 7
+#define SADB_X_AALG_RIPEMD160HMAC 8
+#define SADB_X_AALG_NULL 251 /* kame */
+#define SADB_AALG_MAX 251
+
+/* Encryption algorithms */
+#define SADB_EALG_NONE 0
+#define SADB_EALG_DESCBC 2
+#define SADB_EALG_3DESCBC 3
+#define SADB_X_EALG_CASTCBC 6
+#define SADB_X_EALG_BLOWFISHCBC 7
+#define SADB_EALG_NULL 11
+#define SADB_X_EALG_AESCBC 12
+#define SADB_EALG_MAX 12
+
+/* Compression algorithms */
+#define SADB_X_CALG_NONE 0
+#define SADB_X_CALG_OUI 1
+#define SADB_X_CALG_DEFLATE 2
+#define SADB_X_CALG_LZS 3
+#define SADB_X_CALG_LZJH 4
+#define SADB_X_CALG_MAX 4
+
+/* Extension Header values */
+#define SADB_EXT_RESERVED 0
+#define SADB_EXT_SA 1
+#define SADB_EXT_LIFETIME_CURRENT 2
+#define SADB_EXT_LIFETIME_HARD 3
+#define SADB_EXT_LIFETIME_SOFT 4
+#define SADB_EXT_ADDRESS_SRC 5
+#define SADB_EXT_ADDRESS_DST 6
+#define SADB_EXT_ADDRESS_PROXY 7
+#define SADB_EXT_KEY_AUTH 8
+#define SADB_EXT_KEY_ENCRYPT 9
+#define SADB_EXT_IDENTITY_SRC 10
+#define SADB_EXT_IDENTITY_DST 11
+#define SADB_EXT_SENSITIVITY 12
+#define SADB_EXT_PROPOSAL 13
+#define SADB_EXT_SUPPORTED_AUTH 14
+#define SADB_EXT_SUPPORTED_ENCRYPT 15
+#define SADB_EXT_SPIRANGE 16
+#define SADB_X_EXT_KMPRIVATE 17
+#define SADB_X_EXT_POLICY 18
+#define SADB_X_EXT_SA2 19
+/* The next four entries are for setting up NAT Traversal */
+#define SADB_X_EXT_NAT_T_TYPE 20
+#define SADB_X_EXT_NAT_T_SPORT 21
+#define SADB_X_EXT_NAT_T_DPORT 22
+#define SADB_X_EXT_NAT_T_OA 23
+#define SADB_EXT_MAX 23
+
+/* Identity Extension values */
+#define SADB_IDENTTYPE_RESERVED 0
+#define SADB_IDENTTYPE_PREFIX 1
+#define SADB_IDENTTYPE_FQDN 2
+#define SADB_IDENTTYPE_USERFQDN 3
+#define SADB_IDENTTYPE_MAX 3
+
+#endif /* !(_LINUX_PFKEY2_H) */
diff --git a/tools/vnet/vnet-module/random.c b/tools/vnet/vnet-module/random.c
new file mode 100644
index 0000000000..642937c006
--- /dev/null
+++ b/tools/vnet/vnet-module/random.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+#include "hash_table.h"
+
+#define MODULE_NAME "RANDOM"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** @file
+ * Source of randomness.
+ * Current implementation is not enough.
+ * Needs to be cryptographically strong.
+ */
+
+static unsigned long seed = 0;
+static unsigned long count = 0;
+
+static unsigned long stir(unsigned long *a, unsigned long b){
+ pseudo_des(a, &b);
+ return b;
+}
+
+/** Get one random byte.
+ *
+ * @return random byte
+ */
+int get_random_byte(void){
+ return stir(&seed, ++count);
+}
+
+#if 0
+/* Get some random bytes.
+ *
+ * @param dst destination for the bytes
+ * @param dst_n number of bytes to get
+ */
+void get_random_bytes(void *dst, int dst_n){
+ int i;
+ char *p = (char *)dst;
+ for(i = 0; i < dst_n; i++){
+ *p++ = get_random_byte();
+ }
+}
+#endif
+
+/** Contribute a random byte.
+ *
+ * @param b byte to contribute
+ */
+void add_random_byte(int b){
+ stir(&seed, ++count);
+ stir(&seed, b);
+}
+
+/** Contribute some random bytes.
+ *
+ * @param src bytes to contribute
+ * @param src_n number of bytes
+ */
+void add_random_bytes(const void *src, int src_n){
+ int i;
+ char *p = (char *)src;
+ for(i = 0; i < src_n; i++){
+ add_random_byte(*p++);
+ }
+}
+
+int __init random_module_init(void){
+ int dummy;
+ int tmp = jiffies;
+ seed = (unsigned long)&dummy;
+ add_random_byte(tmp);
+ return 0;
+}
+
+void __exit random_module_exit(void){
+}
+
diff --git a/tools/vnet/vnet-module/random.h b/tools/vnet/vnet-module/random.h
new file mode 100644
index 0000000000..e1f95f8603
--- /dev/null
+++ b/tools/vnet/vnet-module/random.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __VNET_RANDOM_H__
+#define __VNET_RANDOM_H__
+
+extern int get_random_byte(void);
+extern void get_random_bytes(void *dst, int dst_n);
+extern void add_random_byte(int b);
+extern void add_random_bytes(const void *src, int src_n);
+
+extern int random_module_init(void);
+extern void random_module_exit(void);
+
+#endif /* ! __VNET_RANDOM_H__ */
diff --git a/tools/vnet/vnet-module/sa.c b/tools/vnet/vnet-module/sa.c
new file mode 100644
index 0000000000..b0aa67e68f
--- /dev/null
+++ b/tools/vnet/vnet-module/sa.c
@@ -0,0 +1,670 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+
+#include <sa.h>
+#include <sa_algorithm.h>
+#include "hash_table.h"
+#include "allocate.h"
+
+#define MODULE_NAME "IPSEC"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** @file IPSEC Security Association (SA).
+ */
+
+/** Maximum number of protocols.*/
+#define INET_PROTOCOL_MAX 256
+
+/** Table of SA types indexed by protocol. */
+static SAType *sa_type[INET_PROTOCOL_MAX] = {};
+
+/** Hash a protocol number.
+ *
+ * @param protocol protocol number
+ * @return hashcode
+ */
+static inline unsigned char InetProtocol_hash(int protocol){
+ return (protocol) & (INET_PROTOCOL_MAX - 1);
+}
+
+/** Register an SA type.
+ * It is an error if an SA type is already registered for the protocol.
+ *
+ * @param type SA type
+ * @return 0 on success, error code otherwise
+ */
+int SAType_add(SAType *type){
+ int err = -EINVAL;
+ int hash;
+ if(!type) goto exit;
+ hash = InetProtocol_hash(type->protocol);
+ if(sa_type[hash]) goto exit;
+ err = 0;
+ sa_type[hash] = type;
+ exit:
+ return err;
+}
+
+/** Deregister an SA type.
+ * It is an error if no SA type is registered for the protocol.
+ *
+ * @param type SA type
+ * @return 0 on success, error code otherwise
+ */
+int SAType_del(SAType *type){
+ int err = -EINVAL;
+ int hash;
+ if(!type) goto exit;
+ hash = InetProtocol_hash(type->protocol);
+ if(!sa_type[hash]) goto exit;
+ err = 0;
+ sa_type[hash] = NULL;
+ exit:
+ return err;
+}
+
+int SAType_get(int protocol, SAType **type){
+ int err = -ENOENT;
+ int hash;
+ hash = InetProtocol_hash(protocol);
+ *type = sa_type[hash];
+ if(!*type) goto exit;
+ err = 0;
+ exit:
+ return err;
+}
+
+/* Defeat compiler warnings about unused functions. */
+static int sa_key_check(SAKey *key, enum sa_alg_type type) __attribute__((unused));
+static u32 random_spi(void) __attribute__((unused));
+static u32 generate_key(u32 key, u32 offset, u32 spi) __attribute__((unused));
+
+/** Check a key has an acceptable length for an algorithm.
+ *
+ * @param key key
+ * @param type algorithm
+ * @return 0 on success, error code otherwise
+ */
+static int sa_key_check(SAKey *key, enum sa_alg_type type){
+ return 0;
+}
+
+static unsigned long sa_spi_counter = 0;
+
+/** Generate a random spi.
+ * Uses a hashed counter.
+ *
+ * @return spi
+ */
+static u32 random_spi(void){
+ unsigned long left, right = 0;
+ u32 spi;
+ do{
+ left = sa_spi_counter++;
+ pseudo_des(&left, &right);
+ spi = right;
+ } while(!spi);
+ return spi;
+}
+
+/** Mangle some input to generate output.
+ * This is used to derive spis and keying material from secrets,
+ * so it probably ought to be cryptographically strong.
+ * Probably ought to use a good hash (sha1) or cipher (aes).
+ *
+ * @param input input values
+ * @param n number of values
+ * @return mangled value
+ */
+static u32 mangle(u32 input[], int n){
+ unsigned long left = 0, right = 0;
+ int i;
+ for(i=0; i<n; i++){
+ left ^= input[i];
+ pseudo_des(&left, &right);
+ }
+ return (u32)right;
+}
+
+/** Generate a spi for a given protocol and address, using a secret key.
+ * The offset is used when it is necessary to generate more than one spi
+ * for the same protocol and address.
+ *
+ * @param key key
+ * @param offset offset
+ * @param protocol protocol
+ * @param addr IP address
+ * @return spi
+ */
+static u32 generate_spi(u32 key, u32 offset, u32 protocol, u32 addr){
+ u32 input[] = { key, offset, protocol, addr };
+ u32 spi;
+ dprintf(">\n");
+ spi = mangle(input, 4);
+ dprintf("< spi=%x\n", spi);
+ return spi;
+}
+
+/** Generate keying material for a given spi, based on a
+ * secret.
+ *
+ * @param key secret
+ * @param offset offset
+ * @param spi spi
+ * @return keying material
+ */
+static u32 generate_key(u32 key, u32 offset, u32 spi){
+ u32 input[] = { key, offset, spi };
+ return mangle(input, 3);
+}
+
+/** Allocate a spi.
+ * Want to use random ones.
+ * So check for ones not in use.
+ *
+ * When using static keying, both ends need to agree on key.
+ * How does that work? Also, will suddenly get traffic using a spi,
+ * and will have to create SA then. Or need to create in advance.
+ * But can't do that because don't know peers.
+ * When get message on a spi that doesn't exist - do what?
+ * Use a spi related to the destination addr and a secret.
+ * Then receiver can check if spi is ok and create SA on demand.
+ * Use hash of key, protocol, addr to generate. Then have to check
+ * for in-use because of potential collisions. Receiver can do the
+ * same hash and check spi is in usable range. Then derive keys from
+ * the spi (using another secret).
+ *
+ * @param key spi generation key
+ * @param protocol protocol
+ * @param addr IP address
+ * @param spip return parameter for spi
+ * @return 0 on success, error code otherwise
+ */
+int sa_spi_alloc(u32 key, u32 protocol, u32 addr, u32 *spip){
+ int err = 0;
+ int i = 0, n = 100;
+ u32 spi;
+ for(i = 0; i < n; i++, spi++){
+ spi = generate_spi(key, i, protocol, addr);
+ if(!spi) continue;
+ if(!sa_table_lookup_spi(spi, protocol, addr)){
+ *spip = spi;
+ goto exit;
+ }
+ }
+ err = -ENOMEM;
+ exit:
+ return err;
+}
+
+/** Table of SAs. Indexed by unique id and spi/protocol/addr triple.
+ */
+static HashTable *sa_table = NULL;
+
+static u32 sa_id = 1;
+
+/** Hash an SA id.
+ *
+ * @param id SA id
+ * @return hashcode
+ */
+static inline Hashcode sa_table_hash_id(u32 id){
+ return hash_ul(id);
+}
+
+/** Hash SA spi/protocol/addr.
+ *
+ * @param spi spi
+ * @param protocol protocol
+ * @param addr IP address
+ * @return hashcode
+ */
+static inline Hashcode sa_table_hash_spi(u32 spi, u32 protocol, u32 addr){
+ Hashcode h = 0;
+ h = hash_2ul(spi, protocol);
+ h = hash_hul(h, addr);
+ return h;
+}
+
+/** Test if an SA entry has a given value.
+ *
+ * @param arg contains SA pointer
+ * @param table hashtable
+ * @param entry entry containing SA
+ * @return 1 if it does, 0 otherwise
+ */
+static int sa_table_state_fn(TableArg arg, HashTable *table, HTEntry *entry){
+ return entry->value == arg.ptr;
+}
+
+/** Test if an SA entry has a given id.
+ *
+ * @param arg contains SA id
+ * @param table hashtable
+ * @param entry entry containing SA
+ * @return 1 if it does, 0 otherwise
+ */
+static int sa_table_id_fn(TableArg arg, HashTable *table, HTEntry *entry){
+ SAState *state = entry->value;
+ u32 id = arg.ul;
+ return state->ident.id == id;
+}
+
+/** Test if an SA entry has a given spi/protocol/addr.
+ *
+ * @param arg contains SAIdent pointer
+ * @param table hashtable
+ * @param entry entry containing SA
+ * @return 1 if it does, 0 otherwise
+ */
+static int sa_table_spi_fn(TableArg arg, HashTable *table, HTEntry *entry){
+ SAState *state = entry->value;
+ SAIdent *ident = arg.ptr;
+ return state->ident.spi == ident->spi
+ && state->ident.protocol == ident->protocol
+ && state->ident.addr == ident->addr;
+}
+
+/** Free an SA entry. Decrements the SA refcount and frees the entry.
+ *
+ * @param table containing table
+ * @param entry to free
+ */
+void sa_table_free_fn(HashTable *table, HTEntry *entry){
+ if(!entry) return;
+ if(entry->value){
+ SAState *state = entry->value;
+ SAState_decref(state);
+ }
+ deallocate(entry);
+}
+
+/** Initialize the SA table.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int sa_table_init(void){
+ int err = 0;
+ sa_table = HashTable_new(0);
+ if(!sa_table){
+ err = -ENOMEM;
+ goto exit;
+ }
+ sa_table->entry_free_fn = sa_table_free_fn;
+
+ exit:
+ return err;
+}
+
+void sa_table_exit(void){
+ HashTable_free(sa_table);
+}
+
+/** Remove an SA from the table.
+ *
+ * @param state SA
+ */
+int sa_table_delete(SAState *state){
+ int count = 0;
+ Hashcode h1, h2;
+ TableArg arg = { .ptr = state };
+ // Remove by id.
+ h1 = sa_table_hash_id(state->ident.id);
+ count += HashTable_remove_entry(sa_table, h1, sa_table_state_fn, arg);
+ // Remove by spi/protocol/addr if spi nonzero.
+ if(!state->ident.spi) goto exit;
+ h2 = sa_table_hash_spi(state->ident.spi, state->ident.protocol, state->ident.addr);
+ if(h1 == h2) goto exit;
+ count += HashTable_remove_entry(sa_table, h2, sa_table_state_fn, arg);
+ exit:
+ return count;
+}
+
+/** Add an SA to the table.
+ * The SA is indexed by id and spi/protocol/addr (if the spi is non-zero).
+ *
+ * @param state SA
+ * @return 0 on success, error code otherwise
+ */
+int sa_table_add(SAState *state){
+ int err = 0;
+ Hashcode h1, h2;
+ int entries = 0;
+
+ dprintf(">\n");
+ // Index by id.
+ h1 = sa_table_hash_id(state->ident.id);
+ if(!HashTable_add_entry(sa_table, h1, HKEY(state->ident.id), state)){
+ err = -ENOMEM;
+ goto exit;
+ }
+ entries++;
+ SAState_incref(state);
+ // Index by spi/protocol/addr if spi non-zero.
+ if(state->ident.spi){
+ h2 = sa_table_hash_spi(state->ident.spi, state->ident.protocol, state->ident.addr);
+ if(h1 != h2){
+ if(!HashTable_add_entry(sa_table, h2, HKEY(state->ident.id), state)){
+ err = -ENOMEM;
+ goto exit;
+ }
+ entries++;
+ SAState_incref(state);
+ }
+ }
+ exit:
+ if(err && entries){
+ sa_table_delete(state);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+
+/** Find an SA by spi/protocol/addr.
+ * Increments the SA refcount on success.
+ *
+ * @param spi spi
+ * @param protocol protocol
+ * @param addr IP address
+ * @return SA or NULL
+ */
+SAState * sa_table_lookup_spi(u32 spi, u32 protocol, u32 addr){
+ SAState *state = NULL;
+ Hashcode h;
+ SAIdent id = {
+ .spi = spi,
+ .protocol = protocol,
+ .addr = addr };
+ TableArg arg = { .ptr = &id };
+ HTEntry *entry = NULL;
+
+ h = sa_table_hash_spi(spi, protocol, addr);
+ entry = HashTable_find_entry(sa_table, h, sa_table_spi_fn, arg);
+ if(entry){
+ state = entry->value;
+ SAState_incref(state);
+ }
+ return state;
+}
+
+/** Find an SA by unique id.
+ * Increments the SA refcount on success.
+ *
+ * @param id id
+ * @return SA or NULL
+ */
+SAState * sa_table_lookup_id(u32 id){
+ Hashcode h;
+ TableArg arg = { .ul = id };
+ HTEntry *entry = NULL;
+ SAState *state = NULL;
+
+ dprintf("> id=%u\n", id);
+ h = sa_table_hash_id(id);
+ entry = HashTable_find_entry(sa_table, h, sa_table_id_fn, arg);
+ if(entry){
+ state = entry->value;
+ SAState_incref(state);
+ }
+ dprintf("< state=%p\n", state);
+ return state;
+}
+
+/** Replace an existing SA by another in the table.
+ * The existing SA is not removed if the new one cannot be added.
+ *
+ * @param existing SA to replace
+ * @param state new SA
+ * @return 0 on success, error code otherwise
+ */
+static int sa_table_replace(SAState *existing, SAState *state){
+ int err = 0;
+ // Need check for in-use?
+
+ dprintf(">\n");
+ if(existing->keying.state != SA_STATE_ACQUIRE){
+ err = -EINVAL;
+ goto exit;
+ }
+ // replace it.
+ err = sa_table_add(state);
+ if(err) goto exit;
+ sa_table_delete(existing);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Allocate an SA.
+ *
+ * @return SA or NULL
+ */
+SAState *SAState_alloc(void){
+ SAState *state;
+
+ dprintf(">\n");
+ state = kmalloc(sizeof(SAState), GFP_ATOMIC);
+ if(!state) goto exit;
+ *state = (SAState){};
+ atomic_set(&state->refcount, 1);
+ state->lock = SPIN_LOCK_UNLOCKED;
+ exit:
+ dprintf("< state=%p\n", state);
+ return state;
+}
+
+/** Create an SA in initial state.
+ * It has no spi and its keying state is acquire.
+ * It must have a unique id, protocol and address.
+ * At some point it should get updated with a complete SA.
+ *
+ * @param ident SA identifier
+ * @param statep return parameter for new SA
+ * @return 0 on success, error code otherwise
+ */
+int SAState_init(SAIdent *ident, SAState **statep){
+ int err = 0;
+ SAState *state = NULL;
+
+ if(ident->spi || !ident->id){
+ err = -EINVAL;
+ goto exit;
+ }
+ state = SAState_alloc();
+ if (!state){
+ err = -ENOMEM;
+ goto exit;
+ }
+ state->ident = *ident;
+ state->keying.state = SA_STATE_ACQUIRE;
+ exit:
+ return err;
+}
+
+/** Create a complete SA, with spi and cipher suite.
+ *
+ * @param info SA parameters
+ * @param statep return parameter for new SA
+ * @return 0 on success, error code otherwise
+ */
+int SAState_create(SAInfo *info, SAState **statep){
+ int err = 0;
+ SAState *state = NULL;
+
+ dprintf(">\n");
+ state = SAState_alloc();
+ if (!state){
+ err = -ENOMEM;
+ goto exit;
+ }
+ state->ident = info->ident;
+ state->limits = info->limits;
+ state->digest = info->digest;
+ state->cipher = info->cipher;
+ state->compress = info->compress;
+ state->security = info->security;
+ err = SAType_get(state->ident.protocol, &state->type);
+ if (err) goto exit;
+ err = state->type->init(state, NULL);
+ if (err) goto exit;
+ state->keying.state = SA_STATE_VALID;
+ exit:
+ if(err){
+ SAState_decref(state);
+ state = NULL;
+ }
+ *statep = state;
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Create an SA for the given spi etc.
+ * For now we fix the cipher suite and the keys.
+ * Digest is SHA1 HMAC with a 128-bit key.
+ * Cipher is AES (Rijndael) in CBC mode with a 128-bit key.
+ *
+ * The cipher suite and keys should really come from policy, with the
+ * possibility of negotiating them with the peer (using IKE).
+ * Negotiation creates difficulties though - because the SA cannot
+ * be created immediately we have to be able to queue packets
+ * while the SA is being negotiated.
+ *
+ * @param spi spi
+ * @param protocol protocol
+ * @param addr address
+ * @param sa return parameter for SA
+ * @return 0 on success, error code otherwise
+ */
+int sa_create(int security, u32 spi, u32 protocol, u32 addr, SAState **sa){
+ int err = 0;
+ SAInfo info = {};
+ char *digest_name = "sha1";
+ char *digest_key = "0123456789abcdef";
+ int digest_key_n = strlen(digest_key);
+ char *cipher_name= "aes";
+ char *cipher_key = "0123456789ABCDEF";
+ int cipher_key_n = strlen(cipher_key);
+
+ dprintf("> security=%d spi=%u protocol=%u addr=" IPFMT "\n",
+ security, spi, protocol, NIPQUAD(addr));
+ if(!spi){
+ spi = generate_spi(0, 0, protocol, addr);
+ }
+ dprintf("> info...\n");
+ info.ident.id = sa_id++;
+ info.ident.spi = spi;
+ info.ident.protocol = protocol;
+ info.ident.addr = addr;
+ info.security = security;
+
+ //sa_algorithm_probe_all();
+
+ dprintf("> digest name=%s key_n=%d\n", digest_name, digest_key_n);
+ strcpy(info.digest.name, digest_name);
+ info.digest.bits = digest_key_n * 8;
+ memcpy(info.digest.key, digest_key, digest_key_n);
+
+ if(security & SA_CONF){
+ dprintf("> cipher name=%s key_n=%d\n", cipher_name, cipher_key_n);
+ strcpy(info.cipher.name, cipher_name);
+ info.cipher.bits = cipher_key_n * 8;
+ memcpy(info.cipher.key, cipher_key, cipher_key_n);
+ } else {
+ dprintf("> cipher name=%s key_n=%d\n", "cipher_null", 0);
+ strcpy(info.cipher.name, "cipher_null");
+ info.cipher.bits = 0;
+ memset(info.cipher.key, 0, sizeof(info.cipher.key));
+ }
+
+ err = sa_set(&info, 0, sa);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Create or update an SA.
+ * The SA is added to the table.
+ *
+ * @param info SA parameters
+ * @param update create if zero, update otherwise
+ * @return 0 on success, error code otherwise
+ */
+int sa_set(SAInfo *info, int update, SAState **val){
+ int err = 0;
+ SAState *state = NULL;
+ SAState *existing = NULL;
+
+ dprintf("> info=%p update=%d val=%p\n", info, update, val);
+ existing = sa_table_lookup_id(info->ident.id);
+ if(update && !existing){
+ err = -ENOENT;
+ } else if(!update && existing){
+ err = -EINVAL;
+ }
+ if(err) goto exit;
+ err = SAState_create(info, &state);
+ if (err) goto exit;
+ if(existing){
+ err = sa_table_replace(existing, state);
+ } else {
+ err = sa_table_add(state);
+ }
+ exit:
+ if(existing) SAState_decref(existing);
+ if(val && !err){
+ *val = state;
+ } else {
+ SAState_decref(state);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Delete an SA. Removes it from the SA table.
+ * It is an error if no SA with the given id exists.
+ *
+ * @param id SA id
+ * @return 0 on success, error code otherwise
+ */
+int sa_delete(int id){
+ int err = 0;
+ SAState *state;
+ state = sa_table_lookup_id(id);
+ if (!state){
+ err = -ENOENT;
+ goto exit;
+ }
+ sa_table_delete(state);
+ SAState_decref(state);
+ exit:
+ return err;
+}
diff --git a/tools/vnet/vnet-module/sa.h b/tools/vnet/vnet-module/sa.h
new file mode 100644
index 0000000000..5da76c0bfc
--- /dev/null
+++ b/tools/vnet/vnet-module/sa.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __VNET_SA_H__
+#define __VNET_SA_H__
+
+#include <linux/types.h>
+#include <linux/crypto.h>
+
+#include <tunnel.h>
+
+#ifndef CRYPTO_MAX_KEY_BYTES
+#define CRYPTO_MAX_KEY_BYTES 64
+#define CRYPTO_MAX_KEY_BITS (CRYPTO_MAX_KEY_BYTES * 8)
+#endif
+
+typedef struct SALimits {
+ u64 bytes_soft;
+ u64 bytes_hard;
+ u64 packets_soft;
+ u64 packets_hard;
+} SALimits;
+
+typedef struct SACounts {
+ u64 bytes;
+ u64 packets;
+ u32 integrity_failures;
+} SACounts;
+
+typedef struct SAReplay {
+ int replay;
+ u32 send_seq;
+ u32 recv_seq;
+ u32 bitmap;
+ u32 replay_window;
+} SAReplay;
+
+typedef struct SAKey {
+ char name[CRYPTO_MAX_ALG_NAME];
+ int bits;
+ char key[CRYPTO_MAX_KEY_BYTES];
+} SAKey;
+
+typedef struct SAKeying {
+ u8 state;
+ u8 dying;
+} SAKeying;
+
+typedef struct SAIdent {
+ u32 id;
+ u32 spi;
+ u32 addr;
+ u32 protocol;
+} SAIdent;
+
+struct SAType;
+
+/** Security assocation (SA). */
+typedef struct SAState {
+ atomic_t refcount;
+ spinlock_t lock;
+ /** Identifier. */
+ struct SAIdent ident;
+ /** Security flags. */
+ int security;
+ /** Keying state. */
+ struct SAKeying keying;
+ /** Byte counts etc. */
+ struct SACounts counts;
+ /** Byte limits etc. */
+ struct SALimits limits;
+ /** Replay protection. */
+ struct SAReplay replay;
+ /** Digest algorithm. */
+ struct SAKey digest;
+ /** Cipher algorithm. */
+ struct SAKey cipher;
+ /** Compress algorith. */
+ struct SAKey compress;
+ /** SA type (ESP, AH). */
+ struct SAType *type;
+ /** Data for the SA type to use. */
+ void *data;
+} SAState;
+
+typedef struct SAType {
+ char *name;
+ int protocol;
+ int (*init)(SAState *state, void *args);
+ void (*fini)(SAState *state);
+ int (*recv)(SAState *state, struct sk_buff *skb);
+ int (*send)(SAState *state, struct sk_buff *skb, Tunnel *tunnel);
+ u32 (*size)(SAState *state, int size);
+} SAType;
+
+/** Information needed to create an SA.
+ * Unused algorithms have zero key size.
+ */
+typedef struct SAInfo {
+ /** Identifier. */
+ SAIdent ident;
+ /** Security flags. */
+ int security;
+ /** Digest algorithm and key. */
+ SAKey digest;
+ /** Cipher algorithm and key. */
+ SAKey cipher;
+ /** Compress algorithm and key. */
+ SAKey compress;
+ /** SA lifetime limits. */
+ SALimits limits;
+ /** Replay protection window. */
+ int replay_window;
+} SAInfo;
+
+enum sa_alg_type {
+ SA_ALG_DIGEST = 1,
+ SA_ALG_CIPHER = 2,
+ SA_ALG_COMPRESS = 3,
+};
+
+extern int SAType_add(SAType *type);
+extern int SAType_del(SAType *type);
+extern int SAType_get(int protocol, SAType **type);
+
+extern int sa_table_init(void);
+extern void sa_table_exit(void);
+extern int sa_table_delete(SAState *state);
+extern int sa_table_add(SAState *state);
+extern SAState * sa_table_lookup_spi(u32 spi, u32 protocol, u32 addr);
+extern SAState * sa_table_lookup_id(u32 id);
+
+/** Increment reference count.
+ *
+ * @param sa security association (may be null)
+ */
+static inline void SAState_incref(SAState *sa){
+ if(!sa) return;
+ atomic_inc(&sa->refcount);
+}
+
+/** Decrement reference count, freeing if zero.
+ *
+ * @param sa security association (may be null)
+ */
+static inline void SAState_decref(SAState *sa){
+ if(!sa) return;
+ if(atomic_dec_and_test(&sa->refcount)){
+ sa->type->fini(sa);
+ kfree(sa);
+ }
+}
+
+extern SAState *SAState_alloc(void);
+extern int SAState_init(SAIdent *id, SAState **statep);
+extern int SAState_create(SAInfo *info, SAState **statep);
+
+static inline int SAState_send(SAState *sa, struct sk_buff *skb, Tunnel *tunnel){
+ return sa->type->send(sa, skb, tunnel);
+}
+
+static inline int SAState_recv(SAState *sa, struct sk_buff *skb){
+ return sa->type->recv(sa, skb);
+}
+
+static inline int SAState_size(SAState *sa, int n){
+ return sa->type->size(sa, n);
+}
+
+extern int sa_create(int security, u32 spi, u32 protocol, u32 addr, SAState **sa);
+extern int sa_set(SAInfo *info, int update, SAState **val);
+extern int sa_delete(int id);
+
+enum {
+ SA_AUTH = 1,
+ SA_CONF = 2
+};
+
+enum {
+ SA_STATE_ACQUIRE = 1,
+ SA_STATE_VALID = 2,
+};
+
+#endif /* !__VNET_SA_H__ */
diff --git a/tools/vnet/vnet-module/sa_algorithm.c b/tools/vnet/vnet-module/sa_algorithm.c
new file mode 100644
index 0000000000..d5d1418174
--- /dev/null
+++ b/tools/vnet/vnet-module/sa_algorithm.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/crypto.h>
+#include <linux/sched.h>
+//#include <asm/softirq.h>
+
+#include <sa_algorithm.h>
+
+#define MODULE_NAME "IPSEC"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** @file Tables of supported IPSEC algorithms.
+ * Has tables for digests, ciphers and compression algorithms.
+ */
+
+/*
+ * Algorithms supported by IPsec. These entries contain properties which
+ * are used in key negotiation and sa processing, and are used to verify
+ * that instantiated crypto transforms have correct parameters for IPsec
+ * purposes.
+ */
+
+/** Digests. */
+static SAAlgorithm digest_alg[] = {
+ {
+ .name = "digest_null",
+ .info = {
+ .digest = {
+ .icv_truncbits = 0,
+ .icv_fullbits = 0,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_X_AALG_NULL,
+ .sadb_alg_ivlen = 0,
+ .sadb_alg_minbits = 0,
+ .sadb_alg_maxbits = 0
+ }
+ },
+ {
+ .name = "md5",
+ .info = { .digest = {
+ .icv_truncbits = 96,
+ .icv_fullbits = 128,
+ } },
+ .alg = {
+ .sadb_alg_id = SADB_AALG_MD5HMAC,
+ .sadb_alg_ivlen = 0,
+ .sadb_alg_minbits = 128,
+ .sadb_alg_maxbits = 128
+ }
+ },
+ {
+ .name = "sha1",
+ .info = {
+ .digest = {
+ .icv_truncbits = 96,
+ .icv_fullbits = 160,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_AALG_SHA1HMAC,
+ .sadb_alg_ivlen = 0,
+ .sadb_alg_minbits = 160,
+ .sadb_alg_maxbits = 160
+ }
+ },
+ {
+ .name = "sha256",
+ .info = {
+ .digest = {
+ .icv_truncbits = 128,
+ .icv_fullbits = 256,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_X_AALG_SHA2_256HMAC,
+ .sadb_alg_ivlen = 0,
+ .sadb_alg_minbits = 256,
+ .sadb_alg_maxbits = 256
+ }
+ },
+/* { */
+/* .name = "ripemd160", */
+/* .info = { */
+/* .digest = { */
+/* .icv_truncbits = 96, */
+/* .icv_fullbits = 160, */
+/* } */
+/* }, */
+/* .alg = { */
+/* .sadb_alg_id = SADB_X_AALG_RIPEMD160HMAC, */
+/* .sadb_alg_ivlen = 0, */
+/* .sadb_alg_minbits = 160, */
+/* .sadb_alg_maxbits = 160 */
+/* } */
+/* }, */
+ { /* Terminator */ }
+};
+
+/** Ciphers. */
+static SAAlgorithm cipher_alg[] = {
+ {
+ .name = "cipher_null",
+ .info = {
+ .cipher = {
+ .blockbits = 8,
+ .defkeybits = 0,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_EALG_NULL,
+ .sadb_alg_ivlen = 0,
+ .sadb_alg_minbits = 0,
+ .sadb_alg_maxbits = 0
+ }
+ },
+ {
+ .name = "des",
+ .info = {
+ .cipher = {
+ .blockbits = 64,
+ .defkeybits = 64,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_EALG_DESCBC,
+ .sadb_alg_ivlen = 8,
+ .sadb_alg_minbits = 64,
+ .sadb_alg_maxbits = 64
+ }
+ },
+ {
+ .name = "des3_ede",
+ .info = {
+ .cipher = {
+ .blockbits = 64,
+ .defkeybits = 192,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_EALG_3DESCBC,
+ .sadb_alg_ivlen = 8,
+ .sadb_alg_minbits = 192,
+ .sadb_alg_maxbits = 192
+ }
+ },
+/* { */
+/* .name = "cast128", */ //cast5?
+/* .info = { */
+/* .cipher = { */
+/* .blockbits = 64, */
+/* .defkeybits = 128, */
+/* } */
+/* }, */
+/* .alg = { */
+/* .sadb_alg_id = SADB_X_EALG_CASTCBC, */
+/* .sadb_alg_ivlen = 8, */
+/* .sadb_alg_minbits = 40, */
+/* .sadb_alg_maxbits = 128 */
+/* } */
+/* }, */
+ {
+ .name = "blowfish",
+ .info = {
+ .cipher = {
+ .blockbits = 64,
+ .defkeybits = 128,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_X_EALG_BLOWFISHCBC,
+ .sadb_alg_ivlen = 8,
+ .sadb_alg_minbits = 40,
+ .sadb_alg_maxbits = 448
+ }
+ },
+ {
+ .name = "aes",
+ .info = {
+ .cipher = {
+ .blockbits = 128,
+ .defkeybits = 128,
+ }
+ },
+ .alg = {
+ .sadb_alg_id = SADB_X_EALG_AESCBC,
+ .sadb_alg_ivlen = 8,
+ .sadb_alg_minbits = 128,
+ .sadb_alg_maxbits = 256
+ }
+ },
+ { /* Terminator */ }
+};
+
+/** Compressors. */
+static SAAlgorithm compress_alg[] = {
+ {
+ .name = "deflate",
+ .info = {
+ .compress = {
+ .threshold = 90,
+ }
+ },
+ .alg = { .sadb_alg_id = SADB_X_CALG_DEFLATE }
+ },
+/* { */
+/* .name = "lzs", */
+/* .info = { */
+/* .compress = { */
+/* .threshold = 90, */
+/* } */
+/* }, */
+/* .alg = { .sadb_alg_id = SADB_X_CALG_LZS } */
+/* }, */
+/* { */
+/* .name = "lzjh", */
+/* .info = { */
+/* .compress = { */
+/* .threshold = 50, */
+/* } */
+/* }, */
+/* .alg = { .sadb_alg_id = SADB_X_CALG_LZJH } */
+/* }, */
+ { /* Terminator */ }
+};
+
+static SAAlgorithm *sa_algorithm_by_id(SAAlgorithm *algo, int alg_id) {
+ for( ; algo && algo->name; algo++){
+ if (algo->alg.sadb_alg_id == alg_id) {
+ return (algo->available ? algo : NULL);
+ }
+ }
+ return NULL;
+}
+
+
+static SAAlgorithm *sa_algorithm_by_name(SAAlgorithm *algo, char *name) {
+ if (!name) return NULL;
+ for( ; algo && algo->name; algo++){
+ if (strcmp(name, algo->name) == 0) {
+ return (algo->available ? algo : NULL);
+ }
+ }
+ return NULL;
+}
+
+SAAlgorithm *sa_digest_by_id(int alg_id) {
+ return sa_algorithm_by_id(digest_alg, alg_id);
+}
+
+SAAlgorithm *sa_cipher_by_id(int alg_id) {
+ return sa_algorithm_by_id(cipher_alg, alg_id);
+}
+
+SAAlgorithm *sa_compress_by_id(int alg_id) {
+ return sa_algorithm_by_id(compress_alg, alg_id);
+}
+
+SAAlgorithm *sa_digest_by_name(char *name) {
+ return sa_algorithm_by_name(digest_alg, name);
+}
+
+SAAlgorithm *sa_cipher_by_name(char *name) {
+ return sa_algorithm_by_name(cipher_alg, name);
+}
+
+SAAlgorithm *sa_compress_by_name(char *name) {
+ return sa_algorithm_by_name(compress_alg, name);
+}
+
+SAAlgorithm *sa_digest_by_index(unsigned int idx) {
+ return digest_alg + idx;
+}
+
+SAAlgorithm *sa_cipher_by_index(unsigned int idx) {
+ return cipher_alg + idx;
+}
+
+SAAlgorithm *sa_compress_by_index(unsigned int idx) {
+ return compress_alg + idx;
+}
+
+static void sa_algorithm_probe(SAAlgorithm *algo){
+ int status;
+ dprintf("> algo=%p\n", algo);
+ for( ; algo && algo->name; algo++){
+ dprintf("> algorithm %s...\n", algo->name);
+ status = crypto_alg_available(algo->name, 0);
+ dprintf("> algorithm %s status=%d\n",algo->name, status);
+ if (algo->available != status){
+ algo->available = status;
+ }
+ }
+ dprintf("<\n");
+}
+
+/** Crypto api is broken. When an unregistered algorithm is requested it
+ * tries to load a module of the same name. But not all algorithms are
+ * defined by modules of the same name.
+ */
+static char *crypto_modules[] = {
+ "aes",
+ //"arc4",
+ "blowfish",
+ //"cast5",
+ //"cast6",
+ "crypto_null",
+ "des",
+ //"md4",
+ "md5",
+ //"serpent",
+ "sha1",
+ "sha256",
+ //"sha512",
+ //"twofish",
+ NULL
+};
+
+#include <linux/kmod.h>
+
+static void sa_module_probe(char **modules){
+ char **p;
+ dprintf(">\n");
+ for(p = modules; *p; p++){
+ dprintf("> %s\n", *p);
+ request_module(*p);
+ }
+ dprintf("<\n");
+}
+
+/**
+ * Probe for the availability of crypto algorithms, and set the available
+ * flag for any algorithms found on the system. This is typically called by
+ * pfkey during userspace SA add, update or register.
+ */
+void sa_algorithm_probe_all(void){
+ dprintf("> \n");
+ //BUG_ON(in_softirq());
+ sa_module_probe(crypto_modules);
+ sa_algorithm_probe(digest_alg);
+ sa_algorithm_probe(cipher_alg);
+ sa_algorithm_probe(compress_alg);
+ dprintf("<\n");
+}
diff --git a/tools/vnet/vnet-module/sa_algorithm.h b/tools/vnet/vnet-module/sa_algorithm.h
new file mode 100644
index 0000000000..333481bcb7
--- /dev/null
+++ b/tools/vnet/vnet-module/sa_algorithm.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __VNET_SA_ALGORITHM_H__
+#define __VNET_SA_ALGORITHM_H__
+
+#include <linux/types.h>
+#include <linux/pfkeyv2.h>
+
+typedef struct SADigestInfo {
+ u16 icv_truncbits;
+ u16 icv_fullbits;
+} SADigestInfo;
+
+typedef struct SACipherInfo {
+ u16 blockbits;
+ u16 defkeybits;
+} SACipherInfo;
+
+typedef struct SACompressInfo {
+ u16 threshold;
+} SACompressInfo;
+
+typedef struct SAAlgorithm {
+ char *name;
+ u8 available;
+ union {
+ SADigestInfo digest;
+ SACipherInfo cipher;
+ SACompressInfo compress;
+ } info;
+ struct sadb_alg alg;
+} SAAlgorithm;
+
+extern SAAlgorithm *sa_digest_by_id(int alg_id);
+extern SAAlgorithm *sa_cipher_by_id(int alg_id);
+extern SAAlgorithm *sa_compress_by_id(int alg_id);
+extern SAAlgorithm *sa_digest_by_name(char *name);
+extern SAAlgorithm *sa_cipher_by_name(char *name);
+extern SAAlgorithm *sa_compress_by_name(char *name);
+extern SAAlgorithm *sa_digest_by_index(unsigned int idx);
+extern SAAlgorithm *sa_cipher_by_index(unsigned int idx);
+extern SAAlgorithm *sa_compress_by_index(unsigned int idx);
+extern void sa_algorithm_probe_all(void);
+
+#define MAX_KEY_BITS 512
+
+#endif /* ! __VNET_SA_ALGORITHM_H__ */
diff --git a/tools/vnet/vnet-module/skb_context.c b/tools/vnet/vnet-module/skb_context.c
new file mode 100644
index 0000000000..5a76d7ed89
--- /dev/null
+++ b/tools/vnet/vnet-module/skb_context.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+
+#include <skb_context.h>
+
+#define MODULE_NAME "VNET"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+SkbContext *SkbContext_create(u32 vnet, u32 addr, int protocol, void *data,
+ void (*free_fn)(SkbContext *)){
+ SkbContext *context = NULL;
+
+ context = kmalloc(sizeof(SkbContext), GFP_ATOMIC);
+ if(!context) goto exit;
+ context->vnet = vnet;
+ context->addr = addr;
+ context->protocol = protocol;
+ context->data = data;
+ context->free_fn = free_fn;
+ context->next = NULL;
+ atomic_set(&context ->refcount, 1);
+ exit:
+ return context;
+}
+
+void SkbContext_free(SkbContext *context){
+ if(!context) return;
+ if(context->next) SkbContext_decref(context->next);
+ if(context->free_fn) context->free_fn(context);
+ context->vnet = 0;
+ context->addr = 0;
+ context->protocol = 0;
+ context->free_fn = NULL;
+ context->data = NULL;
+ context->next = NULL;
+ kfree(context);
+}
+
+int SkbContext_push(SkbContext **val, u32 vnet, u32 addr, int protocol,
+ void *data, void (*free_fn)(SkbContext *)){
+ int err = 0;
+ SkbContext *context = NULL;
+
+ dprintf("> vnet=%u addr=%u.%u.%u.%u protocol=%d\n",
+ vnet, NIPQUAD(addr), protocol);
+ context = SkbContext_create(vnet, addr, protocol, data, free_fn);
+ if(!context){
+ err = -ENOMEM;
+ goto exit;
+ }
+ context->next = *val;
+ *val = context;
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int skb_push_context(struct sk_buff *skb, u32 vnet, u32 addr, int protocol,
+ void *data, void (*free_fn)(SkbContext *)){
+ int err = 0;
+ //SkbContext *ctxt = SKB_CONTEXT(skb);
+ dprintf("> skb=%p\n", skb);
+
+ //err = SkbContext_push(&ctxt, vnet, addr, protocol, data, free_fn); //todo fixme
+ //SKB_CONTEXT(skb) = ctxt;//todo fixme
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+
diff --git a/tools/vnet/vnet-module/skb_context.h b/tools/vnet/vnet-module/skb_context.h
new file mode 100644
index 0000000000..10cfac4c3b
--- /dev/null
+++ b/tools/vnet/vnet-module/skb_context.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __VNET_SKB_CONTEXT_H__
+#define __VNET_SKB_CONTEXT_H__
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <asm/atomic.h>
+#include <linux/types.h>
+
+/** Structure used to record inbound processing path for skbs.
+ * For example, the ETHERIP protocol handler can use this to
+ * tell whether an inbound packet came through IPSEC ESP or not.
+ */
+typedef struct SkbContext {
+ u32 vnet;
+ u32 addr;
+ int protocol;
+ void *data;
+ void (*free_fn)(struct SkbContext *);
+ atomic_t refcount;
+ struct SkbContext *next;
+} SkbContext;
+
+/** Decrement the reference count, freeing if zero.
+ *
+ * @param context context (may be null)
+ */
+static inline void SkbContext_decref(SkbContext *context){
+ extern void SkbContext_free(SkbContext *context);
+ if(!context) return;
+ if(atomic_dec_and_test(&context->refcount)){
+ SkbContext_free(context);
+ }
+}
+
+/** Increment the reference count.
+ *
+ * @param context context (may be null)
+ */
+static inline void SkbContext_incref(SkbContext *context){
+ if(!context) return;
+ atomic_inc(&context->refcount);
+}
+
+extern SkbContext *SkbContext_create(u32 vnet, u32 addr, int protocol, void *data,
+ void (*free_fn)(SkbContext *));
+
+extern int SkbContext_push(SkbContext **val, u32 vnet, u32 addr, int protocol,
+ void *data, void (*free_fn)(SkbContext *));
+
+struct sk_buff;
+extern int skb_push_context(struct sk_buff *skb, u32 vnet, u32 addr, int protocol,
+ void *data, void (*free_fn)(SkbContext *));
+
+//todo: fixme
+#define SKB_CONTEXT(_skb) ((SkbContext *)(&(_skb)->cb[0]))
+
+#endif /* !__VNET_SKB_CONTEXT_H__ */
diff --git a/tools/vnet/vnet-module/skb_util.c b/tools/vnet/vnet-module/skb_util.c
new file mode 100644
index 0000000000..c9742d6f51
--- /dev/null
+++ b/tools/vnet/vnet-module/skb_util.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/version.h>
+
+#include <asm/scatterlist.h>
+#include <linux/crypto.h>
+#include <linux/pfkeyv2.h>
+#include <linux/random.h>
+
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+
+#include <varp.h>
+#include <skb_util.h>
+
+#define MODULE_NAME "VNET"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+static const int DEBUG_SCATTERLIST = 0;
+static const int DEBUG_SKB = 0;
+
+//============================================================================
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#define SET_SCATTER_ADDR(sg, addr) do{} while(0)
+#else
+#define SET_SCATTER_ADDR(sg, addr) (sg).address = (addr)
+#endif
+
+/** Make enough room in an skb for extra header and trailer.
+ *
+ * @param pskb return parameter for expanded skb
+ * @param skb skb
+ * @param head_n required headroom
+ * @param tail_n required tailroom
+ * @return 0 on success, error code otherwise
+ */
+int skb_make_room(struct sk_buff **pskb, struct sk_buff *skb, int head_n, int tail_n){
+ int err = 0;
+ int has_headroom = (head_n <= skb_headroom(skb));
+ int has_tailroom = (tail_n <= skb_tailroom(skb));
+ int writeable = !skb_cloned(skb) && !skb_shared(skb);
+
+ dprintf("> skb=%p headroom=%d head_n=%d tailroom=%d tail_n=%d\n",
+ skb,
+ skb_headroom(skb), head_n,
+ skb_tailroom(skb), tail_n);
+ if(writeable && has_headroom && has_tailroom){
+ // There's room! Reuse it.
+ *pskb = skb;
+ } else if(writeable && has_tailroom){
+ // Tailroom, no headroom. Expand header the way GRE does.
+ struct sk_buff *new_skb = skb_realloc_headroom(skb, head_n + 16);
+ if(!new_skb){
+ err = -ENOMEM;
+ goto exit;
+ }
+ dev_kfree_skb(skb);
+ *pskb = new_skb;
+ } else {
+ // No room. Expand. There may be more efficient ways to do
+ // this, but this is simple and correct.
+ struct sk_buff *new_skb = skb_copy_expand(skb, head_n + 16, tail_n, GFP_ATOMIC);
+ if(!new_skb){
+ err = -ENOMEM;
+ goto exit;
+ }
+ dev_kfree_skb(skb);
+ *pskb = new_skb;
+ }
+ dprintf("> skb=%p headroom=%d head_n=%d tailroom=%d tail_n=%d\n",
+ *pskb,
+ skb_headroom(*pskb), head_n,
+ skb_tailroom(*pskb), tail_n);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Copy some data bits from a kernel buffer to an skb.
+ * Derived in the obvious way from skb_copy_bits().
+ */
+int skb_put_bits(const struct sk_buff *skb, int offset, void *src, int len)
+{
+ int i, copy;
+ int start = skb->len - skb->data_len;
+
+ if (offset > (int)skb->len-len)
+ goto fault;
+
+ /* Copy header. */
+ if ((copy = start-offset) > 0) {
+ if (copy > len)
+ copy = len;
+ memcpy(skb->data + offset, src, copy);
+ if ((len -= copy) == 0)
+ return 0;
+ offset += copy;
+ src += copy;
+ }
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ int end;
+
+ BUG_TRAP(start <= offset+len);
+
+ end = start + skb_shinfo(skb)->frags[i].size;
+ if ((copy = end-offset) > 0) {
+ u8 *vaddr;
+
+ if (copy > len)
+ copy = len;
+
+ vaddr = kmap_skb_frag(&skb_shinfo(skb)->frags[i]);
+ memcpy(vaddr + skb_shinfo(skb)->frags[i].page_offset + offset - start,
+ src,
+ copy);
+ kunmap_skb_frag(vaddr);
+
+ if ((len -= copy) == 0)
+ return 0;
+ offset += copy;
+ src += copy;
+ }
+ start = end;
+ }
+
+ if (skb_shinfo(skb)->frag_list) {
+ struct sk_buff *list;
+
+ for (list = skb_shinfo(skb)->frag_list; list; list=list->next) {
+ int end;
+
+ BUG_TRAP(start <= offset+len);
+
+ end = start + list->len;
+ if ((copy = end-offset) > 0) {
+ if (copy > len)
+ copy = len;
+ if (skb_put_bits(list, offset-start, src, copy))
+ goto fault;
+ if ((len -= copy) == 0)
+ return 0;
+ offset += copy;
+ src += copy;
+ }
+ start = end;
+ }
+ }
+ if (len == 0)
+ return 0;
+
+ fault:
+ return -EFAULT;
+}
+
+/** Add some space to the end of a (possibly fragmented) skb.
+ *
+ * Only works with Xen output skbs. Output skbs have 1 frag, and we
+ * add another frag for the extra space.
+ *
+ * @param skb skb
+ * @param n number of bytes to add
+ * @return 0 on success, error code otherwise
+ *
+ * @todo fixme
+ */
+int pskb_put(struct sk_buff *skb, int n){
+ int err = 0;
+ if(1 || skb_is_nonlinear(skb)){
+ struct skb_shared_info *info = skb_shinfo(skb);
+ char *ptr = NULL;
+
+ if(info->nr_frags >= MAX_SKB_FRAGS){
+ err = -ENOMEM;
+ goto exit;
+ }
+ ptr = kmalloc(n, GFP_ATOMIC);
+ if(!ptr){
+ err = -ENOMEM;
+ goto exit;
+ }
+ info->nr_frags++;
+ info->frags[info->nr_frags - 1].page = virt_to_page(ptr);
+ info->frags[info->nr_frags - 1].page_offset = ((unsigned long)ptr & ~PAGE_MASK);
+ info->frags[info->nr_frags - 1].size = n;
+
+ skb->data_len += n;
+ skb->len += n;
+ } else {
+ __skb_put(skb, n);
+ }
+ exit:
+ if(err) dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Print some bits of an skb.
+ *
+ * @param skb to print
+ * @param offset byte offset to start printing at
+ * @param n number of bytes to print
+ */
+void skb_print_bits(struct sk_buff *skb, int offset, int n){
+ int chunk = 16;
+ int i, k;
+ u8 buff[chunk];
+ if(!DEBUG_SKB) return;
+ while(n){
+ k = (n > chunk ? chunk : n);
+ skb_copy_bits(skb, offset, buff, k);
+ printk("%03d ", offset);
+ for(i=0; i<k; i++){
+ if(i == 8)printk(" ");
+ printk(":%02x", buff[i] & 0xff);
+ }
+ printk(" \n");
+ n -= k;
+ offset += k;
+ }
+}
+
+/** Print a buffer.
+ *
+ * @param buf to print
+ * @param n number of bytes to print
+ */
+void buf_print(char *buf, int n){
+ int i;
+ for(i=0; i<n; i++){
+ if( i % 16 == 0) printk("\n%04d ", i);
+ else if(i % 8 == 0) printk(" ");
+ printk(":%02x", buf[i] & 0xff);
+ }
+ printk(" %04d\n", n);
+}
+
+/** Remove some space from the tail of an skb.
+ *
+ * @todo fixme: Do we need to handle frags?
+ */
+void *skb_trim_tail(struct sk_buff *skb, int n){
+ skb->tail -= n;
+ skb->len -= n;
+ return skb->tail;
+}
+
+// #define BUG_TRAP(x)
+// if(!(x)){ printk("KERNEL: assertion (" #x ") failed at " __FILE__ "(%d)\n", __LINE__); }
+
+/** Convert a (possibly fragmented) skb into a scatter list.
+ *
+ * @param skb skb to convert
+ * @param sg scatterlist to set up
+ * @param sg_n size of sg on input, number of elements set on output
+ * @param offset offset into data to start at
+ * @param len number of bytes
+ * @return 0 on success, error code otherwise
+ */
+int skb_scatterlist(struct sk_buff *skb, struct scatterlist *sg, int *sg_n,
+ int offset, int len){
+ int err = 0;
+ int start; // No. of bytes copied so far (where next copy starts).
+ int size; // Size of the next chunk.
+ int end; // Where the next chunk ends (start + size).
+ int copy; // Number of bytes to copy in one operation.
+ int sg_i = 0; // Index into sg.
+ int i;
+
+ if(DEBUG_SCATTERLIST){
+ dprintf("> offset=%d len=%d (end=%d), skb len=%d,\n",
+ offset, len, offset+len, skb->len);
+ }
+ start = 0;
+ size = skb_headlen(skb);
+ end = start + size;
+ copy = end - offset;
+ if(copy > 0){
+ char *p;
+ if(copy > len) copy = len;
+ if(sg_i >= *sg_n){
+ err = -EINVAL;
+ goto exit;
+ }
+ p = skb->data + offset;
+ SET_SCATTER_ADDR(sg[sg_i], NULL);
+ sg[sg_i].page = virt_to_page(p);
+ sg[sg_i].offset = ((unsigned long)p & ~PAGE_MASK);
+ sg[sg_i].length = copy;
+ if(DEBUG_SCATTERLIST){
+ dprintf("> sg_i=%d .page=%p .offset=%u .length=%d\n",
+ sg_i, sg[sg_i].page, sg[sg_i].offset, sg[sg_i].length);
+ }
+ sg_i++;
+ if((len -= copy) == 0) goto exit;
+ offset += copy;
+ }
+ start = end;
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++){
+ BUG_TRAP(start <= offset + len);
+ size = skb_shinfo(skb)->frags[i].size;
+ end = start + size;
+ copy = end - offset;
+ if(copy > 0){
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ if(copy > len) copy = len;
+ if(sg_i >= *sg_n){
+ err = -EINVAL;
+ goto exit;
+ }
+ SET_SCATTER_ADDR(sg[sg_i], NULL);
+ sg[sg_i].page = frag->page;
+ sg[sg_i].offset = frag->page_offset + offset - start;
+ sg[sg_i].length = copy;
+ if(DEBUG_SCATTERLIST){
+ dprintf("> sg_i=%d .page=%p .offset=%u .length=%d\n",
+ sg_i, sg[sg_i].page, sg[sg_i].offset, sg[sg_i].length);
+ }
+ sg_i++;
+ if((len -= copy) == 0) goto exit;
+ offset += copy;
+ }
+ start = end;
+ }
+ exit:
+ if(!err) *sg_n = sg_i;
+ if(len) wprintf("> len=%d\n", len);
+ if(len) BUG();
+ if(err) dprintf("< err=%d sg_n=%d\n", err, *sg_n);
+ return err;
+}
+
+struct arpheader
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+#if 1
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+#endif
+
+};
+
+void print_skb_data(char *msg, int count, struct sk_buff *skb, u8 *data, int len)
+{
+ static int skb_count = 1000000;
+ u8 *ptr, *end;
+ u32 src_addr, dst_addr;
+ // Transport layer header.
+ union {
+ struct tcphdr *th;
+ struct udphdr *uh;
+ struct icmphdr *icmph;
+ struct igmphdr *igmph;
+ struct iphdr *ipiph;
+ unsigned char *raw;
+ } h;
+ // Network layer header.
+ union {
+ struct iphdr *iph;
+ struct ipv6hdr *ipv6h;
+ struct arpheader *arph;
+ struct ipxhdr *ipxh;
+ unsigned char *raw;
+ } nh;
+ // Link layer header.
+ union {
+ struct ethhdr *ethernet;
+ unsigned char *raw;
+ } mac;
+ int protocol;
+ if(!count) count = ++skb_count;
+ if(!msg) msg = (char *)__FUNCTION__;
+ if(!data){
+ printk("%s.%d> null data\n", msg, count);
+ return;
+ }
+ ptr = data;
+ end = data + len;
+ mac.raw = ptr;
+ ptr += sizeof(struct ethhdr);
+ if(ptr > end){ printk("***MAC:"); goto exit; }
+ protocol = ntohs(mac.ethernet->h_proto);
+ nh.raw = ptr;
+
+ printk("%s.%d> type=%d protocol=0x%x\n",
+ msg, count, skb->pkt_type, htons(skb->protocol));
+ if(1){
+ printk("%s.%d> %p mac src=" MACFMT " dst=" MACFMT "\n",
+ msg, count, data,
+ MAC6TUPLE(mac.ethernet->h_source),
+ MAC6TUPLE(mac.ethernet->h_dest));
+ }
+
+ switch(protocol){
+ case ETH_P_ARP:
+ ptr += sizeof(struct arpheader);
+ if(ptr > end){ printk("***ARP:"); goto exit; }
+ if(0){
+ printk("%s.%d> ARP hrd=%d, pro=%d, hln=%d, pln=%d, op=%d\n",
+ msg, count,
+ nh.arph->ar_hrd, nh.arph->ar_pro, nh.arph->ar_hln,
+ nh.arph->ar_pln, nh.arph->ar_op);
+ }
+ memcpy(&src_addr, nh.arph->ar_sip, 4);
+ src_addr = ntohl(src_addr);
+ memcpy(&dst_addr, nh.arph->ar_tip, 4);
+ dst_addr = ntohl(dst_addr);
+ printk("%s.%d> ARP HW src=" MACFMT " dst=" MACFMT "\n",
+ msg, count, MAC6TUPLE(nh.arph->ar_sha), MAC6TUPLE(nh.arph->ar_tha));
+ printk("%s.%d> ARP IP src=" IPFMT " dst=" IPFMT "\n",
+ msg, count, HIPQUAD(src_addr), HIPQUAD(dst_addr));
+ break;
+ case ETH_P_IP: {
+ u16 src_port, dst_port;
+ if(ptr + sizeof(struct iphdr) > end){ printk("***IP:"); goto exit; }
+ src_addr = ntohl(nh.iph->saddr);
+ dst_addr = ntohl(nh.iph->daddr);
+ if(1){
+ printk("%s.%d> IP proto=%d src=" IPFMT " dst=" IPFMT "\n",
+ msg, count, nh.iph->protocol,
+ HIPQUAD(src_addr), HIPQUAD(dst_addr));
+ printk("%s.%d> IP tot_len=%u len=%d\n",
+ msg, count, nh.iph->tot_len & 0xffff, len - ETH_HLEN);
+ }
+ ptr += (nh.iph->ihl * 4);
+ if(ptr > end){ printk ("***IP: len"); goto exit; }
+ h.raw = ptr;
+ switch(nh.iph->protocol){
+ case IPPROTO_TCP:
+ ptr += sizeof(struct tcphdr);
+ if(ptr > end){ printk("***TCP:"); goto exit; }
+ src_port = ntohs(h.th->source);
+ dst_port = ntohs(h.th->dest);
+ printk("%s.%d> TCP src=" IPFMT ":%u dst=" IPFMT ":%u\n",
+ msg, count,
+ HIPQUAD(src_addr), src_port,
+ HIPQUAD(dst_addr), dst_port);
+ break;
+ case IPPROTO_UDP:
+ ptr += sizeof(struct udphdr);
+ if(ptr > end){ printk("***UDP:"); goto exit; }
+ src_port = ntohs(h.uh->source);
+ dst_port = ntohs(h.uh->dest);
+ printk("%s.%d> UDP src=" IPFMT ":%u dst=" IPFMT ":%u\n",
+ msg, count,
+ HIPQUAD(src_addr), src_port,
+ HIPQUAD(dst_addr), dst_port);
+ break;
+ default:
+ printk("%s.%d> IP %d src=" IPFMT " dst=" IPFMT "\n",
+ msg, count,
+ nh.iph->protocol, HIPQUAD(src_addr), HIPQUAD(dst_addr));
+ break;
+ }
+ break; }
+ case ETH_P_IPV6:
+ printk("%s.%d> IPv6\n", msg, count);
+ break;
+ case ETH_P_IPX:
+ printk("%s.%d> IPX\n", msg, count);
+ break;
+ default:
+ printk("%s.%d> protocol=%d\n", msg, count, protocol);
+ break;
+ }
+ return;
+ exit:
+ printk("%s.%d> %s: skb problem\n", msg, count, __FUNCTION__);
+ printk("%s.%d> %s: data=%p end=%p(%d) ptr=%p(%d) eth=%d arp=%d ip=%d\n",
+ msg, count, __FUNCTION__,
+ data, end, end - data, ptr, ptr - data,
+ sizeof(struct ethhdr), sizeof(struct arphdr), sizeof(struct iphdr));
+ return;
+}
+
diff --git a/tools/vnet/vnet-module/skb_util.h b/tools/vnet/vnet-module/skb_util.h
new file mode 100644
index 0000000000..d3e9a1e6d9
--- /dev/null
+++ b/tools/vnet/vnet-module/skb_util.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _VNET_SKB_UTIL_H_
+#define _VNET_SKB_UTIL_H_
+
+struct sk_buff;
+struct scatterlist;
+
+extern int skb_make_room(struct sk_buff **pskb, struct sk_buff *skb, int head_n, int tail_n);
+
+extern int skb_put_bits(const struct sk_buff *skb, int offset, void *src, int len);
+
+extern int pskb_put(struct sk_buff *skb, int n);
+
+extern void skb_print_bits(struct sk_buff *skb, int offset, int n);
+
+extern void buf_print(char *buf, int n);
+
+extern void *skb_trim_tail(struct sk_buff *skb, int n);
+
+extern int skb_scatterlist(struct sk_buff *skb, struct scatterlist *sg,
+ int *sg_n, int offset, int len);
+
+extern void print_skb_data(char *msg, int count, struct sk_buff *skb, u8 *data, int len);
+
+
+#endif
diff --git a/tools/vnet/vnet-module/tunnel.c b/tools/vnet/vnet-module/tunnel.c
new file mode 100644
index 0000000000..2ea261bf6f
--- /dev/null
+++ b/tools/vnet/vnet-module/tunnel.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+
+#include <tunnel.h>
+#include <vnet.h>
+#include <varp.h>
+#include "hash_table.h"
+
+#define MODULE_NAME "VNET"
+//#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+void Tunnel_print(Tunnel *tunnel){
+ if(tunnel){
+ printk("Tunnel<%p base=%p ref=%02d type=%s>\n",
+ tunnel,
+ tunnel->base,
+ atomic_read(&tunnel->refcount),
+ tunnel->type->name);
+ if(tunnel->base){
+ Tunnel_print(tunnel->base);
+ }
+ } else {
+ printk("Tunnel<%p base=%p ref=%02d type=%s>\n",
+ NULL, NULL, 0, "ip");
+ }
+}
+
+int Tunnel_create(TunnelType *type, u32 vnet, u32 addr, Tunnel *base, Tunnel **val){
+ int err = 0;
+ Tunnel *tunnel = NULL;
+ dprintf("> type=%s vnet=%d addr=" IPFMT " base=%s\n",
+ type->name, vnet, NIPQUAD(addr), (base ? base->type->name : "ip"));
+ if(!type || !type->open || !type->send || !type->close){
+ err = -EINVAL;
+ goto exit;
+ }
+ tunnel = kmalloc(sizeof(Tunnel), GFP_ATOMIC);
+ if(!tunnel){
+ err = -ENOMEM;
+ goto exit;
+ }
+ atomic_set(&tunnel->refcount, 1);
+ tunnel->key.vnet = vnet;
+ tunnel->key.addr = addr;
+ tunnel->type = type;
+ tunnel->data = NULL;
+ tunnel->send_stats = (TunnelStats){};
+ Tunnel_incref(base);
+ tunnel->base = base;
+ err = type->open(tunnel);
+ exit:
+ if(err && tunnel){
+ Tunnel_decref(tunnel);
+ tunnel = NULL;
+ }
+ *val = tunnel;
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int Tunnel_open(TunnelType *type, u32 vnet, u32 addr, Tunnel *base, Tunnel **tunnel){
+ int err = 0;
+
+ dprintf(">\n");
+ err = Tunnel_create(type, vnet, addr, base, tunnel);
+ if(err) goto exit;
+ err = Tunnel_add(*tunnel);
+ exit:
+ if(err){
+ Tunnel_decref(*tunnel);
+ *tunnel = NULL;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+void TunnelStats_update(TunnelStats *stats, int len, int err){
+ dprintf(">len=%d err=%d\n", len, err);
+ if(err){
+ stats->dropped_bytes += len;
+ stats->dropped_packets++;
+ } else {
+ stats->bytes += len;
+ stats->packets++;
+ }
+ dprintf("<\n");
+}
+
+/** Table of tunnels, indexed by vnet and addr. */
+HashTable *tunnel_table = NULL;
+
+static inline Hashcode tunnel_table_key_hash_fn(void *k){
+ TunnelKey *key = k;
+ Hashcode h = 0;
+ h = hash_2ul(key->vnet, key->addr);
+ return h;
+}
+
+static int tunnel_table_key_equal_fn(void *k1, void *k2){
+ TunnelKey *key1 = k1;
+ TunnelKey *key2 = k2;
+ return (key1->vnet == key2->vnet)
+ && (key1->addr == key2->addr);
+}
+
+static void tunnel_table_entry_free_fn(HashTable *table, HTEntry *entry){
+ Tunnel *tunnel;
+ if(!entry) return;
+ tunnel = entry->value;
+ //dprintf(">\n"); Tunnel_print(tunnel);
+ Tunnel_decref(tunnel);
+ HTEntry_free(entry);
+}
+
+int Tunnel_init(void){
+ int err = 0;
+ dprintf(">\n");
+ tunnel_table = HashTable_new(0);
+ if(!tunnel_table){
+ err = -ENOMEM;
+ goto exit;
+ }
+ tunnel_table->entry_free_fn = tunnel_table_entry_free_fn;
+ tunnel_table->key_hash_fn = tunnel_table_key_hash_fn;
+ tunnel_table->key_equal_fn = tunnel_table_key_equal_fn;
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Lookup tunnel state by vnet and destination.
+ *
+ * @param vnet vnet
+ * @param addr destination address
+ * @return tunnel state or NULL
+ */
+Tunnel * Tunnel_lookup(u32 vnet, u32 addr){
+ Tunnel *tunnel = NULL;
+ TunnelKey key = {.vnet = vnet, .addr = addr };
+ dprintf(">\n");
+ tunnel = HashTable_get(tunnel_table, &key);
+ Tunnel_incref(tunnel);
+ dprintf("< tunnel=%p\n", tunnel);
+ return tunnel;
+}
+
+int Tunnel_add(Tunnel *tunnel){
+ int err = 0;
+ dprintf(">\n");
+ if(HashTable_add(tunnel_table, tunnel, tunnel)){
+ Tunnel_incref(tunnel);
+ } else {
+ err = -ENOMEM;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int Tunnel_del(Tunnel *tunnel){
+ return HashTable_remove(tunnel_table, tunnel);
+}
+
+/** Do tunnel send processing on a packet.
+ *
+ * @param tunnel tunnel state
+ * @param skb packet
+ * @return 0 on success, error code otherwise
+ */
+int Tunnel_send(Tunnel *tunnel, struct sk_buff *skb){
+ int err = 0;
+ int len;
+ dprintf("> tunnel=%p skb=%p\n", tunnel, skb);
+ len = skb->len;
+ if(tunnel){
+ dprintf("> type=%s type->send...\n", tunnel->type->name);
+ err = tunnel->type->send(tunnel, skb);
+ // Must not refer to skb after sending - might have been freed.
+ TunnelStats_update(&tunnel->send_stats, len, err);
+ } else {
+ struct net_device *dev = NULL;
+ err = vnet_get_device(DEVICE, &dev);
+ if(err) goto exit;
+ skb->dev = dev;
+ err = skb_xmit(skb);
+ dev_put(dev);
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int __init tunnel_module_init(void){
+ return Tunnel_init();
+}
+
+void __exit tunnel_module_exit(void){
+}
diff --git a/tools/vnet/vnet-module/tunnel.h b/tools/vnet/vnet-module/tunnel.h
new file mode 100644
index 0000000000..e2241e82d4
--- /dev/null
+++ b/tools/vnet/vnet-module/tunnel.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __VNET_TUNNEL_H__
+#define __VNET_TUNNEL_H__
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <asm/atomic.h>
+
+struct sk_buff;
+struct Tunnel;
+
+typedef struct TunnelType {
+ const char *name;
+ int (*open)(struct Tunnel *tunnel);
+ int (*send)(struct Tunnel *tunnel, struct sk_buff *skb);
+ void (*close)(struct Tunnel *tunnel);
+} TunnelType;
+
+typedef struct TunnelStats {
+ int bytes;
+ int packets;
+ int dropped_bytes;
+ int dropped_packets;
+} TunnelStats;
+
+typedef struct TunnelKey {
+ u32 vnet;
+ u32 addr;
+} TunnelKey;
+
+typedef struct Tunnel {
+ /** Key identifying the tunnel. Must be first. */
+ struct TunnelKey key;
+ /** Reference count. */
+ atomic_t refcount;
+ /** Tunnel type. */
+ struct TunnelType *type;
+ /** Statistics. */
+ struct TunnelStats send_stats;
+ /** Type-dependent state. */
+ void *data;
+ /** Underlying tunnel (may be null). */
+ struct Tunnel *base;
+} Tunnel;
+
+extern void Tunnel_print(Tunnel *tunnel);
+
+/** Decrement the reference count, freeing if zero.
+ *
+ * @param tunnel tunnel (may be null)
+ */
+static inline void Tunnel_decref(Tunnel *tunnel){
+ if(!tunnel) return;
+ if(atomic_dec_and_test(&tunnel->refcount)){
+ printk("%s> Closing tunnel:\n", __FUNCTION__);
+ Tunnel_print(tunnel);
+ tunnel->type->close(tunnel);
+ Tunnel_decref(tunnel->base);
+ kfree(tunnel);
+ }
+}
+
+/** Increment the reference count.
+ *
+ * @param tunnel tunnel (may be null)
+ */
+static inline void Tunnel_incref(Tunnel *tunnel){
+ if(!tunnel) return;
+ atomic_inc(&tunnel->refcount);
+}
+
+extern int Tunnel_init(void);
+extern Tunnel * Tunnel_lookup(u32 vnet, u32 addr);
+extern int Tunnel_add(Tunnel *tunnel);
+extern int Tunnel_del(Tunnel *tunnel);
+extern int Tunnel_send(Tunnel *tunnel, struct sk_buff *skb);
+
+extern int Tunnel_create(TunnelType *type, u32 vnet, u32 addr, Tunnel *base, Tunnel **tunnelp);
+extern int Tunnel_open(TunnelType *type, u32 vnet, u32 addr, Tunnel *base, Tunnel **tunnelp);
+
+extern int tunnel_module_init(void);
+extern void tunnel_module_exit(void);
+
+#endif /* !__VNET_TUNNEL_H__ */
diff --git a/tools/vnet/vnet-module/varp.c b/tools/vnet/vnet-module/varp.c
new file mode 100644
index 0000000000..f7bdf81d97
--- /dev/null
+++ b/tools/vnet/vnet-module/varp.c
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/version.h>
+
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/udp.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <asm/semaphore.h>
+
+#include <tunnel.h>
+#include <vnet.h>
+#include <vif.h>
+#include <varp.h>
+#include <if_varp.h>
+
+#include "allocate.h"
+#include "hash_table.h"
+#include "sys_net.h"
+#include "sys_string.h"
+
+#define MODULE_NAME "VARP"
+//#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+// The 'ethernet' field in the skb->mac union went away.
+#define MAC_ETH(_skb) ((struct ethhdr *)(_skb)->mac.raw)
+#else
+#define MAC_ETH(_skb) ((_skb)->mac.ethernet)
+#endif
+
+/** @file VARP: Virtual ARP.
+ *
+ * Handles virtual ARP requests for vnet/vmac.
+ */
+
+/*
+
+Varp uses UDP on port 1798.
+
+on domain up: ?
+ send varp.announce { id, vmac, vnet, coa } for each vif
+ that haven't announced before, or has changed.
+ install vif entries in local table.
+
+on varp.announce{ id, vmac, vnet, coa }:
+ update VARP entry for vmac x vnet if have one, reset ttl.
+
+on varp.request { id, vmac, vnet }:
+ if have a vif for the requested vmac/vnet,
+ reply with varp.announce{ id, vmac, vnet, coa }
+
+on timer:
+ traverse VARP table, flush old entries.
+
+on probe timer:
+ probe again if not out of tries.
+ if out of tries invalidate entry.
+
+*/
+
+/** Time-to-live of varp entries (in jiffies).*/
+#define VARP_ENTRY_TTL (60*HZ)
+
+/** Maximum number of varp probes to make. */
+#define VARP_PROBE_MAX 5
+
+/** Interval between varp probes (in jiffies). */
+#define VARP_PROBE_INTERVAL (3*HZ)
+
+/** Maximum number of queued skbs for a varp entry. */
+#define VARP_QUEUE_MAX 16
+
+/** Number of buckets in the varp table (must be prime). */
+#define VARP_TABLE_BUCKETS 3001
+
+/** Varp entry states. */
+enum {
+ VARP_STATE_INCOMPLETE = 1,
+ VARP_STATE_REACHABLE = 2,
+ VARP_STATE_FAILED = 3
+};
+
+/** Varp entry flags. */
+enum {
+ VARP_FLAG_PROBING = 1,
+ VARP_FLAG_PERMANENT = 2,
+};
+
+/** Key for varp entries. */
+typedef struct VarpKey {
+ /** Vnet id (host order). */
+ u32 vnet;
+ /** Virtual MAC address. */
+ Vmac vmac;
+} VarpKey;
+
+/** An entry in the varp cache. */
+typedef struct VarpEntry {
+ /** Key for the entry. */
+ VarpKey key;
+ /** Care-of address for the key. */
+ u32 addr;
+ /** Last-updated timestamp. */
+ unsigned long timestamp;
+ /** State. */
+ short state;
+ /** Flags. */
+ short flags;
+ /** Reference count. */
+ atomic_t refcount;
+ /** Lock. */
+ rwlock_t lock;
+ /** How many probes have been made. */
+ atomic_t probes;
+ /** Probe timer. */
+ struct timer_list timer;
+ void (*error)(struct VarpEntry *ventry, struct sk_buff *skb);
+ /** Outbound skb queue. */
+ struct sk_buff_head queue;
+ /** Maximum size of the queue. */
+ int queue_max;
+
+ int locks;
+} VarpEntry;
+
+/** The varp cache. Varp entries indexed by VarpKey. */
+typedef struct VarpTable {
+
+ HashTable *table;
+
+ /** Sweep timer. */
+ struct timer_list timer;
+
+ /** Lock. Need to use a semaphore instead of a spinlock because
+ * some operations under the varp table lock can schedule - and
+ * you mustn't hold a spinlock when scheduling.
+ */
+ struct semaphore lock;
+
+} VarpTable;
+
+/** The varp cache. */
+static VarpTable *varp_table = NULL;
+
+/** Module parameter for the multicast address. */
+static char *varp_mcaddr = NULL;
+
+/** Multicast address (network order). */
+u32 varp_mcast_addr = 0;
+
+/** Unicast address (network order). */
+u32 varp_ucast_addr = 0;
+
+/** UDP port (network order). */
+u16 varp_port = 0;
+
+/** Network device to use. */
+char *varp_device = DEVICE;
+
+#define VarpTable_read_lock(z, flags) do{ (flags) = 0; down(&(z)->lock); } while(0)
+#define VarpTable_read_unlock(z, flags) do{ (flags) = 0; up(&(z)->lock); } while(0)
+#define VarpTable_write_lock(z, flags) do{ (flags) = 0; down(&(z)->lock); } while(0)
+#define VarpTable_write_unlock(z, flags) do{ (flags) = 0; up(&(z)->lock); } while(0)
+
+#define VarpEntry_lock(ventry, flags) write_lock_irqsave(&(ventry)->lock, (flags))
+#define VarpEntry_unlock(ventry, flags) write_unlock_irqrestore(&(ventry)->lock, (flags))
+
+void VarpTable_sweep(VarpTable *z, int all);
+void VarpTable_print(VarpTable *z);
+
+/** Print the varp cache (if debug on).
+ */
+void varp_dprint(void){
+#ifdef DEBUG
+ VarpTable_print(varp_table);
+#endif
+}
+
+/** Print varp info and the varp cache.
+ */
+void varp_print(void){
+ printk(KERN_INFO "=== VARP ===============================================================\n");
+ printk(KERN_INFO "varp_device %s\n", varp_device);
+ printk(KERN_INFO "varp_mcast_addr " IPFMT "\n", NIPQUAD(varp_mcast_addr));
+ printk(KERN_INFO "varp_ucast_addr " IPFMT "\n", NIPQUAD(varp_ucast_addr));
+ printk(KERN_INFO "varp_port %d\n", ntohs(varp_port));
+ VarpTable_print(varp_table);
+ printk(KERN_INFO "========================================================================\n");
+}
+
+/** Lookup a network device by name.
+ *
+ * @param name device name
+ * @param dev return parameter for the device
+ * @return 0 on success, error code otherwise
+ */
+int vnet_get_device(const char *name, struct net_device **dev){
+ int err = 0;
+ *dev = dev_get_by_name(name);
+ if(!*dev){
+ err = -ENETDOWN;
+ }
+ return err;
+}
+
+/** Get the source address from a device.
+ *
+ * @param dev device
+ * @param addr return parameter for address
+ * @return 0 on success, error code otherwise
+ */
+int vnet_get_device_address(struct net_device *dev, u32 *addr){
+ int err = 0;
+ struct in_device *in_dev;
+
+ //printk("%s>\n", __FUNCTION__);
+ in_dev = in_dev_get(dev);
+ if(!in_dev){
+ err = -EIO;
+ goto exit;
+ }
+ *addr = in_dev->ifa_list->ifa_address;
+ in_dev_put(in_dev);
+ exit:
+ //printk("%s< err=%d\n", __FUNCTION__, err);
+ return err;
+}
+
+#ifndef LL_RESERVED_SPACE
+#define HH_DATA_MOD 16
+#define LL_RESERVED_SPACE(dev) \
+ ((dev->hard_header_len & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD)
+#endif
+
+/** Send a varp protocol message.
+ *
+ * @param opcode varp opcode (host order)
+ * @param dev device (may be null)
+ * @param skb skb being replied to (may be null)
+ * @param vnet vnet id (in host order)
+ * @param vmac vmac (in network order)
+ * @return 0 on success, error code otherwise
+ */
+int varp_send(u16 opcode, struct net_device *dev, struct sk_buff *skbin,
+ u32 vnet, Vmac *vmac){
+ int err = 0;
+ int link_n = 0;
+ int ip_n = sizeof(struct iphdr);
+ int udp_n = sizeof(struct udphdr);
+ int varp_n = sizeof(VarpHdr);
+ struct sk_buff *skbout = NULL;
+ struct in_device *in_dev = NULL;
+ VarpHdr *varph = NULL;
+ u8 macbuf[6] = {};
+ u8 *smac, *dmac;
+ u32 saddr, daddr;
+ u16 sport, dport;
+
+ dmac = macbuf;
+ dprintf("> opcode=%d vnet=%d vmac=" MACFMT "\n",
+ opcode, ntohl(vnet), MAC6TUPLE(vmac->mac));
+ if(!dev){
+ //todo: should use routing for daddr to get device.
+ err = vnet_get_device(varp_device, &dev);
+ if(err) goto exit;
+ }
+ link_n = LL_RESERVED_SPACE(dev);
+ in_dev = in_dev_get(dev);
+ if(!in_dev) goto exit;
+
+ smac = dev->dev_addr;
+ saddr = in_dev->ifa_list->ifa_address;
+
+ if(skbin){
+ dmac = MAC_ETH(skbin)->h_source;
+ sport = skbin->h.uh->dest;
+ daddr = skbin->nh.iph->saddr;
+ //dport = skbin->h.uh->source;
+ dport = varp_port;
+ } else {
+ if(!in_dev) goto exit;
+ if(MULTICAST(varp_mcast_addr)){
+ daddr = varp_mcast_addr;
+ ip_eth_mc_map(daddr, dmac);
+ } else {
+ daddr = in_dev->ifa_list->ifa_broadcast;
+ dmac = dev->broadcast;
+ }
+ sport = varp_port;
+ dport = varp_port;
+ }
+ in_dev_put(in_dev);
+
+ dprintf("> smac=" MACFMT " dmac=" MACFMT "\n", MAC6TUPLE(smac), MAC6TUPLE(dmac));
+ dprintf("> saddr=" IPFMT " daddr=" IPFMT "\n", NIPQUAD(saddr), NIPQUAD(daddr));
+ dprintf("> sport=%u dport=%u\n", ntohs(sport), ntohs(dport));
+
+ skbout = alloc_skb(link_n + ip_n + udp_n + varp_n, GFP_ATOMIC);
+ if (!skbout){
+ err = -ENOMEM;
+ goto exit;
+ }
+ skbout->dev = dev;
+ skb_reserve(skbout, link_n);
+ skbout->protocol = htons(ETH_P_IP);
+
+ // Device header. Pushes device header on front of skb.
+ if (dev->hard_header){
+ err = dev->hard_header(skbout, dev, ETH_P_IP, dmac, smac, skbout->len);
+ if(err < 0) goto exit;
+ skbout->mac.raw = skbout->data;
+ }
+
+ // IP header.
+ skbout->nh.raw = skb_put(skbout, ip_n);
+ skbout->nh.iph->version = 4;
+ skbout->nh.iph->ihl = ip_n / 4;
+ skbout->nh.iph->tos = 0;
+ skbout->nh.iph->tot_len = htons(ip_n + udp_n + varp_n);
+ skbout->nh.iph->id = 0;
+ skbout->nh.iph->frag_off = 0;
+ skbout->nh.iph->ttl = 64;
+ skbout->nh.iph->protocol = IPPROTO_UDP;
+ skbout->nh.iph->saddr = saddr;
+ skbout->nh.iph->daddr = daddr;
+ skbout->nh.iph->check = 0;
+
+ // UDP header.
+ skbout->h.raw = skb_put(skbout, udp_n);
+ skbout->h.uh->source = sport;
+ skbout->h.uh->dest = dport;
+ skbout->h.uh->len = htons(udp_n + varp_n);
+ skbout->h.uh->check = 0;
+
+ // Varp header.
+ varph = (void*)skb_put(skbout, varp_n);
+ *varph = (VarpHdr){};
+ varph->vnetmsghdr.id = htons(VARP_ID);
+ varph->vnetmsghdr.opcode = htons(opcode);
+ varph->vnet = htonl(vnet);
+ varph->vmac = *vmac;
+ varph->addr = saddr;
+
+ err = skb_xmit(skbout);
+
+ exit:
+ if(err && skbout) kfree_skb(skbout);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Send a varp request for the vnet and destination mac of a packet.
+ *
+ * @param skb packet
+ * @param vnet vnet (in host order)
+ * @return 0 on success, error code otherwise
+ */
+int varp_solicit(struct sk_buff *skb, int vnet){
+ int err = 0;
+ dprintf("> skb=%p\n", skb);
+ varp_dprint();
+ err = varp_send(VARP_OP_REQUEST, NULL, NULL,
+ vnet, (Vmac*)MAC_ETH(skb)->h_dest);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/* Test some flags.
+ *
+ * @param z varp entry
+ * @param flags to test
+ * @return nonzero if flags set
+ */
+int VarpEntry_get_flags(VarpEntry *z, int flags){
+ return z->flags & flags;
+}
+
+/** Set some flags.
+ *
+ * @param z varp entry
+ * @param flags to set
+ * @param set set flags on if nonzero, off if zero
+ * @return new flags value
+ */
+int VarpEntry_set_flags(VarpEntry *z, int flags, int set){
+ if(set){
+ z->flags |= flags;
+ } else {
+ z->flags &= ~flags;
+ }
+ return z->flags;
+}
+
+/** Print a varp entry.
+ *
+ * @param ventry varp entry
+ */
+void VarpEntry_print(VarpEntry *ventry){
+ if(ventry){
+ char *c, *d;
+ switch(ventry->state){
+ case VARP_STATE_INCOMPLETE: c = "INC"; break;
+ case VARP_STATE_REACHABLE: c = "RCH"; break;
+ case VARP_STATE_FAILED: c = "FLD"; break;
+ default: c = "UNK"; break;
+ }
+ d = (VarpEntry_get_flags(ventry, VARP_FLAG_PROBING) ? "P" : " ");
+
+ printk(KERN_INFO "VENTRY(%p ref=%1d %s %s vnet=%d vmac=" MACFMT " addr=" IPFMT " q=%d t=%lu)\n",
+ ventry,
+ atomic_read(&ventry->refcount),
+ c, d,
+ ventry->key.vnet,
+ MAC6TUPLE(ventry->key.vmac.mac),
+ NIPQUAD(ventry->addr),
+ skb_queue_len(&ventry->queue),
+ ventry->timestamp);
+ } else {
+ printk("VENTRY: Null!\n");
+ }
+}
+
+/** Free a varp entry.
+ *
+ * @param z varp entry
+ */
+void VarpEntry_free(VarpEntry *z){
+ if(!z) return;
+ deallocate(z);
+}
+
+/** Increment reference count.
+ *
+ * @param z varp entry (may be null)
+ */
+void VarpEntry_incref(VarpEntry *z){
+ if(!z) return;
+ atomic_inc(&z->refcount);
+ //dprintf("> "); VarpEntry_print(z);
+}
+
+/** Decrement reference count, freeing if zero.
+ *
+ * @param z varp entry (may be null)
+ */
+void VarpEntry_decref(VarpEntry *z){
+ if(!z) return;
+ //dprintf("> "); VarpEntry_print(z);
+ if(atomic_dec_and_test(&z->refcount)){
+ //dprintf("> freeing %p...\n", z);
+ VarpEntry_free(z);
+ }
+}
+
+/** Call the error handler.
+ *
+ * @param ventry varp entry
+ */
+void VarpEntry_error(VarpEntry *ventry){
+ struct sk_buff *skb;
+ skb = skb_peek(&ventry->queue);
+ if(!skb) return;
+ if(ventry->error) ventry->error(ventry, skb);
+ skb_queue_purge(&ventry->queue);
+}
+
+/** Schedule the varp entry timer.
+ * Must increment the reference count before doing
+ * this the first time, so the ventry won' be freed
+ * before the timer goes off.
+ *
+ * @param ventry varp entry
+ */
+void VarpEntry_schedule(VarpEntry *ventry){
+ unsigned long now = jiffies;
+ ventry->timer.expires = now + VARP_PROBE_INTERVAL;
+ add_timer(&ventry->timer);
+}
+
+/** Function called when a varp entry timer goes off.
+ * If the entry is still incomplete, carries on probing.
+ * Otherwise stops probing.
+ *
+ * @param arg ventry
+ */
+static void varp_timer_fn(unsigned long arg){
+ unsigned long flags;
+ VarpEntry *ventry = (VarpEntry *)arg;
+ struct sk_buff *skb = NULL;
+ int locked = 0, probing = 0;
+
+ dprintf(">\n"); //VarpEntry_print(ventry);
+ VarpEntry_lock(ventry, flags);
+ locked = 1;
+ if(ventry->state == VARP_STATE_REACHABLE){
+ // Do nothing.
+ } else {
+ // Probe if haven't run out of tries, otherwise fail.
+ if(atomic_read(&ventry->probes) < VARP_PROBE_MAX){
+ probing = 1;
+ VarpEntry_schedule(ventry);
+ skb = skb_peek(&ventry->queue);
+ if(skb){
+ dprintf("> skbs in queue - solicit\n");
+ atomic_inc(&ventry->probes);
+ VarpEntry_unlock(ventry, flags);
+ locked = 0;
+ varp_solicit(skb, ventry->key.vnet);
+ } else {
+ dprintf("> empty queue.\n");
+ }
+ } else {
+ dprintf("> Out of probes: FAILED\n");
+ VarpEntry_error(ventry);
+ ventry->state = VARP_STATE_FAILED;
+ }
+ }
+ VarpEntry_set_flags(ventry, VARP_FLAG_PROBING, probing);
+ if(locked) VarpEntry_unlock(ventry, flags);
+ if(!probing) VarpEntry_decref(ventry);
+ dprintf("<\n");
+}
+
+/** Default error function for varp entries.
+ *
+ * @param ventry varp entry
+ * @param skb packet dropped because of error
+ */
+static void varp_error_fn(VarpEntry *ventry, struct sk_buff *skb){
+}
+
+/** Create a varp entry. Initializes the internal state.
+ *
+ * @param vnet vnet id
+ * @param vmac virtual MAC address (copied)
+ * @return ventry or null
+ */
+VarpEntry * VarpEntry_new(u32 vnet, Vmac *vmac){
+ VarpEntry *z = ALLOCATE(VarpEntry);
+ if(z){
+ unsigned long now = jiffies;
+
+ atomic_set(&z->refcount, 1);
+ z->lock = RW_LOCK_UNLOCKED;
+ z->state = VARP_STATE_INCOMPLETE;
+ z->queue_max = VARP_QUEUE_MAX;
+ skb_queue_head_init(&z->queue);
+ init_timer(&z->timer);
+ z->timer.data = (unsigned long)z;
+ z->timer.function = varp_timer_fn;
+ z->timestamp = now;
+ z->error = varp_error_fn;
+
+ z->key.vnet = vnet;
+ z->key.vmac = *vmac;
+ }
+ return z;
+}
+
+/** Hash function for keys in the varp cache.
+ * Hashes the vnet id and mac.
+ *
+ * @param k key (VarpKey)
+ * @return hashcode
+ */
+Hashcode varp_key_hash_fn(void *k){
+ VarpKey *key = k;
+ Hashcode h;
+ h = hash_2ul(key->vnet,
+ (key->vmac.mac[0] << 24) |
+ (key->vmac.mac[1] << 16) |
+ (key->vmac.mac[2] << 8) |
+ (key->vmac.mac[3] ));
+ h = hash_hul(h,
+ (key->vmac.mac[4] << 8) |
+ (key->vmac.mac[5] ));
+ return h;
+}
+
+/** Test equality for keys in the varp cache.
+ * Compares vnet and mac.
+ *
+ * @param k1 key to compare (VarpKey)
+ * @param k2 key to compare (VarpKey)
+ * @return 1 if equal, 0 otherwise
+ */
+int varp_key_equal_fn(void *k1, void *k2){
+ VarpKey *key1 = k1;
+ VarpKey *key2 = k2;
+ return (key1->vnet == key2->vnet)
+ && (memcmp(key1->vmac.mac, key2->vmac.mac, ETH_ALEN) == 0);
+}
+
+/** Free an entry in the varp cache.
+ *
+ * @param table containing table
+ * @param entry entry to free
+ */
+static void varp_entry_free_fn(HashTable *table, HTEntry *entry){
+ VarpEntry *ventry;
+ if(!entry) return;
+ ventry = entry->value;
+ if(ventry) VarpEntry_decref(ventry);
+ HTEntry_free(entry);
+}
+
+/** Free the whole varp cache.
+ * Dangerous.
+ *
+ * @param z varp cache
+ */
+void VarpTable_free(VarpTable *z){
+ unsigned long flags;
+ if(!z) return;
+ VarpTable_write_lock(z, flags);
+ del_timer(&z->timer);
+ z->timer.data = 0;
+ if(z->table) HashTable_free(z->table);
+ VarpTable_write_unlock(z, flags);
+ deallocate(z);
+}
+
+/** Schedule the varp table timer.
+ *
+ * @param z varp table
+ */
+void VarpTable_schedule(VarpTable *z){
+ unsigned long now = jiffies;
+ z->timer.expires = now + VARP_ENTRY_TTL;
+ add_timer(&z->timer);
+}
+
+/** Function called when the varp table timer goes off.
+ * Sweeps old varp cache entries and reschedules itself.
+ *
+ * @param arg varp table
+ */
+static void varp_table_timer_fn(unsigned long arg){
+ VarpTable *z = (VarpTable *)arg;
+ //dprintf("> z=%p\n", z);
+ if(z){
+ VarpTable_sweep(z, 0);
+ VarpTable_schedule(z);
+ }
+ //dprintf("<\n");
+}
+
+/** Print a varp table.
+ *
+ * @param z table
+ */
+void VarpTable_print(VarpTable *z){
+ HashTable_for_decl(entry);
+ VarpEntry *ventry;
+ unsigned long flags, vflags;
+
+ //dprintf(">\n");
+ VarpTable_read_lock(z, flags);
+ HashTable_for_each(entry, varp_table->table){
+ ventry = entry->value;
+ VarpEntry_lock(ventry, vflags);
+ VarpEntry_print(ventry);
+ VarpEntry_unlock(ventry, vflags);
+ }
+ VarpTable_read_unlock(z, flags);
+ //dprintf("<\n");
+}
+
+/** Create a varp table.
+ *
+ * @return new table or null
+ */
+VarpTable * VarpTable_new(void){
+ int err = -ENOMEM;
+ VarpTable *z = NULL;
+
+ z = ALLOCATE(VarpTable);
+ if(!z) goto exit;
+ z->table = HashTable_new(VARP_TABLE_BUCKETS);
+ if(!z->table) goto exit;
+ z->table->key_equal_fn = varp_key_equal_fn;
+ z->table->key_hash_fn = varp_key_hash_fn;
+ z->table->entry_free_fn = varp_entry_free_fn;
+ init_MUTEX(&z->lock);
+ init_timer(&z->timer);
+ z->timer.data = (unsigned long)z;
+ z->timer.function = varp_table_timer_fn;
+ VarpTable_schedule(z);
+ err = 0;
+ exit:
+ if(err){
+ VarpTable_free(z);
+ z = NULL;
+ }
+ return z;
+}
+
+/** Add a new entry to the varp table.
+ *
+ * @param z table
+ * @param vnet vnet id
+ * @param vmac virtual MAC address (copied)
+ * @return new entry or null
+ */
+VarpEntry * VarpTable_add(VarpTable *z, u32 vnet, Vmac *vmac){
+ int err = -ENOMEM;
+ VarpEntry *ventry;
+ HTEntry *entry;
+ unsigned long flags;
+
+ ventry = VarpEntry_new(vnet, vmac);
+ if(!ventry) goto exit;
+ //dprintf("> "); VarpEntry_print(ventry);
+ VarpTable_write_lock(z, flags);
+ entry = HashTable_add(z->table, ventry, ventry);
+ VarpTable_write_unlock(z, flags);
+ if(!entry) goto exit;
+ VarpEntry_incref(ventry);
+ err = 0;
+ exit:
+ if(err){
+ VarpEntry_free(ventry);
+ ventry = NULL;
+ }
+ return ventry;
+}
+
+/** Remove an entry from the varp table.
+ *
+ * @param z table
+ * @param ventry entry to remove
+ * @return removed count
+ */
+int VarpTable_remove(VarpTable *z, VarpEntry *ventry){
+ return HashTable_remove(z->table, ventry);
+}
+
+/** Lookup an entry in the varp table.
+ *
+ * @param z table
+ * @param vnet vnet id
+ * @param vmac virtual MAC addres
+ * @return entry found or null
+ */
+VarpEntry * VarpTable_lookup(VarpTable *z, u32 vnet, Vmac *vmac){
+ unsigned long flags;
+ VarpKey key = { .vnet = vnet, .vmac = *vmac };
+ VarpEntry *ventry;
+ VarpTable_read_lock(z, flags);
+ ventry = HashTable_get(z->table, &key);
+ VarpTable_read_unlock(z, flags);
+ if(ventry) VarpEntry_incref(ventry);
+ return ventry;
+}
+
+/** Handle output for a reachable ventry.
+ * Send the skb using the tunnel to the care-of address.
+ *
+ * @param ventry varp entry
+ * @param skb skb to send
+ * @return 0 on success, error code otherwise
+ */
+int VarpEntry_send(VarpEntry *ventry, struct sk_buff *skb){
+ int err = 0;
+ unsigned long flags = 0;
+ u32 addr;
+
+ dprintf("> skb=%p\n", skb);
+ addr = ventry->addr;
+ VarpEntry_unlock(ventry, flags);
+ err = vnet_tunnel_send(ventry->key.vnet, addr, skb);
+ VarpEntry_lock(ventry, flags);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Handle output for a non-reachable ventry. Send messages to complete it.
+ * If the entry is still incomplete, queue the skb, otherwise
+ * send it. If the queue is full, dequeue and free an old skb to
+ * make room for the new one.
+ *
+ * @param ventry varp entry
+ * @param skb skb to send
+ * @return 0 on success, error code otherwise
+ */
+int VarpEntry_resolve(VarpEntry *ventry, struct sk_buff *skb){
+ int err = 0;
+ unsigned long flags = 0;
+
+ dprintf("> skb=%p\n", skb); //VarpEntry_print(ventry);
+ ventry->state = VARP_STATE_INCOMPLETE;
+ atomic_set(&ventry->probes, 1);
+ if(!VarpEntry_get_flags(ventry, VARP_FLAG_PROBING)){
+ VarpEntry_set_flags(ventry, VARP_FLAG_PROBING, 1);
+ VarpEntry_incref(ventry);
+ VarpEntry_schedule(ventry);
+ }
+ VarpEntry_unlock(ventry, flags);
+ varp_solicit(skb, ventry->key.vnet);
+ VarpEntry_lock(ventry, flags);
+
+ if(ventry->state == VARP_STATE_INCOMPLETE){
+ if(skb_queue_len(&ventry->queue) >= ventry->queue_max){
+ struct sk_buff *oldskb;
+ oldskb = ventry->queue.next;
+ __skb_unlink(oldskb, &ventry->queue);
+ dprintf("> purging skb=%p\n", oldskb);
+ kfree_skb(oldskb);
+ }
+ __skb_queue_tail(&ventry->queue, skb);
+ } else {
+ err = VarpEntry_send(ventry, skb);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Handle output for a ventry. Resolves the ventry
+ * if necessary.
+ *
+ * @param ventry varp entry
+ * @param skb skb to send
+ * @return 0 on success, error code otherwise
+ */
+int VarpEntry_output(VarpEntry *ventry, struct sk_buff *skb){
+ int err = 0;
+
+ switch(ventry->state){
+ case VARP_STATE_REACHABLE:
+ err = VarpEntry_send(ventry, skb);
+ break;
+ default:
+ err = VarpEntry_resolve(ventry, skb);
+ break;
+ }
+ return err;
+}
+
+/** Process the output queue for a ventry. Sends the queued skbs if
+ * the ventry is reachable, otherwise drops them.
+ *
+ * @param ventry varp entry
+ */
+void VarpEntry_process_queue(VarpEntry *ventry){
+ struct sk_buff *skb;
+ for( ; ; ){
+ if(ventry->state != VARP_STATE_REACHABLE) break;
+ skb = __skb_dequeue(&ventry->queue);
+ if(!skb) break;
+ VarpEntry_output(ventry, skb);
+ }
+ skb_queue_purge(&ventry->queue);
+}
+
+/** Update a ventry. Sets the address and state to those given
+ * and sets the timestamp to 'now'.
+ *
+ * @param ventry varp entry
+ * @param addr care-of address
+ * @param state state
+ * @return 0 on success, error code otherwise
+ */
+int VarpEntry_update(VarpEntry *ventry, u32 addr, int state){
+ int err = 0;
+ unsigned long now = jiffies;
+ unsigned long flags;
+
+ dprintf("> addr=" IPFMT " state=%d\n", NIPQUAD(addr), state);
+ //VarpEntry_print(ventry);
+ VarpEntry_lock(ventry, flags);
+ if(VarpEntry_get_flags(ventry, VARP_FLAG_PERMANENT)) goto exit;
+ ventry->addr = addr;
+ ventry->timestamp = now;
+ ventry->state = state;
+ VarpEntry_process_queue(ventry);
+ exit:
+ //dprintf("> "); VarpEntry_print(ventry);
+ VarpEntry_unlock(ventry, flags);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int VarpTable_update(VarpTable *z, int vnet, Vmac *vmac, u32 addr,
+ int state, int force){
+ int err = 0;
+ VarpEntry *ventry;
+
+ dprintf("> vnet=%d mac=" MACFMT " addr=" IPFMT " state=%d force=%d\n",
+ vnet, MAC6TUPLE(vmac->mac), NIPQUAD(addr), state, force);
+ ventry = VarpTable_lookup(z, vnet, vmac);
+ if(force && !ventry){
+ dprintf("> No entry, adding\n");
+ ventry = VarpTable_add(z, vnet, vmac);
+ }
+ if(ventry){
+ dprintf("> Updating\n");
+ err = VarpEntry_update(ventry, addr, state);
+ VarpEntry_decref(ventry);
+ } else {
+ dprintf("> No entry found\n");
+ err = -ENOENT;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Update the ventry corresponding to the given varp header.
+ *
+ * @param z table
+ * @param varph varp header
+ * @param state state
+ * @return 0 on success, -ENOENT if no entry found
+ */
+int VarpTable_update_entry(VarpTable *z, VarpHdr *varph, int state){
+ return VarpTable_update(z, ntohl(varph->vnet), &varph->vmac, varph->addr, state, 0);
+}
+
+int varp_update(int vnet, unsigned char *vmac, u32 addr){
+ if(!varp_table){
+ return -ENOSYS;
+ }
+ return VarpTable_update(varp_table, vnet, (Vmac*)vmac, addr,
+ VARP_STATE_REACHABLE, 1);
+}
+
+/** Put old varp entries into the incomplete state.
+ * Permanent entries are not changed.
+ * If 'all' is non-zero, all non-permanent entries
+ * are put into the incomplete state, regardless of age.
+ *
+ * @param z table
+ * @param all reset all entries if non-zero
+ */
+void VarpTable_sweep(VarpTable *z, int all){
+ HashTable_for_decl(entry);
+ VarpEntry *ventry;
+ unsigned long now = jiffies;
+ unsigned long old = now - VARP_ENTRY_TTL;
+ unsigned long flags, vflags;
+
+ //dprintf(">\n");
+ VarpTable_read_lock(z, flags);
+ HashTable_for_each(entry, varp_table->table){
+ ventry = entry->value;
+ VarpEntry_lock(ventry, vflags);
+ if(!VarpEntry_get_flags(ventry, VARP_FLAG_PERMANENT) &&
+ (all || (ventry->timestamp < old))){
+ VarpEntry_process_queue(ventry);
+ ventry->state = VARP_STATE_INCOMPLETE;
+ }
+ VarpEntry_unlock(ventry, vflags);
+ }
+ VarpTable_read_unlock(z, flags);
+ //dprintf("<\n");
+}
+
+/** Handle a varp request. Look for a vif with the requested
+ * vnet and vmac. If find one, reply with the vnet, vmac and our
+ * address. Otherwise do nothing.
+ *
+ * @param skb incoming message
+ * @param varph varp message
+ * @return 0 if ok, -ENOENT if no matching vif, or error code
+ */
+int varp_handle_request(struct sk_buff *skb, VarpHdr *varph){
+ int err = -ENOENT;
+ u32 vnet;
+ Vmac *vmac;
+ Vif *vif = NULL;
+
+ dprintf(">\n");
+ vnet = ntohl(varph->vnet);
+ vmac = &varph->vmac;
+ dprintf("> vnet=%d vmac=" MACFMT "\n", vnet, MAC6TUPLE(vmac->mac));
+ if(vif_lookup(vnet, vmac, &vif)) goto exit;
+ varp_send(VARP_OP_ANNOUNCE, skb->dev, skb, vnet, vmac);
+ vif_decref(vif);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Announce the vnet and vmac of a vif (gratuitous varp).
+ *
+ * @param dev device to send on (may be null)
+ * @param vif vif
+ * @return 0 on success, error code otherwise
+ */
+int varp_announce_vif(struct net_device *dev, Vif *vif){
+ int err = 0;
+ dprintf(">\n");
+ if(!varp_table){
+ err = -ENOSYS;
+ goto exit;
+ }
+ err = varp_send(VARP_OP_ANNOUNCE, dev, NULL, vif->vnet, &vif->vmac);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Handle a varp announce message.
+ * Update the matching ventry if we have one.
+ *
+ * @param skb incoming message
+ * @param varp message
+ * @return 0 if OK, -ENOENT if no matching entry
+ */
+int varp_handle_announce(struct sk_buff *skb, VarpHdr *varph){
+ int err = 0;
+
+ dprintf(">\n");
+ err = VarpTable_update_entry(varp_table, varph, VARP_STATE_REACHABLE);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Handle an incoming varp message.
+ *
+ * @param skb incoming message
+ * @return 0 if OK, error code otherwise
+ */
+int varp_handle_message(struct sk_buff *skb){
+ // Assume h. nh set, skb->data point after udp hdr (at varphdr).
+ int err = -EINVAL, mine = 0;
+ VarpHdr *varph = (void*)(skb->h.uh + 1);
+
+ dprintf(">\n");
+ if(!varp_table){
+ err = -ENOSYS;
+ goto exit;
+ }
+ if(MULTICAST(skb->nh.iph->daddr) &&
+ (skb->nh.iph->daddr != varp_mcast_addr)){
+ // Ignore multicast packets not addressed to us.
+ err = 0;
+ dprintf("> daddr=" IPFMT " mcaddr=" IPFMT "\n",
+ NIPQUAD(skb->nh.iph->daddr), NIPQUAD(varp_mcast_addr));
+ goto exit;
+ }
+ if(skb->len < sizeof(*varph)){
+ wprintf("> Varp msg too short: %d < %d\n", skb->len, sizeof(*varph));
+ goto exit;
+ }
+ mine = 1;
+ if(varph->vnetmsghdr.id != htons(VARP_ID)){
+ // It's not varp at all - ignore it.
+ wprintf("> Unknown id: %d \n", ntohs(varph->vnetmsghdr.id));
+ goto exit;
+ }
+ if(1){
+ dprintf("> saddr=" IPFMT " daddr=" IPFMT "\n",
+ NIPQUAD(skb->nh.iph->saddr), NIPQUAD(skb->nh.iph->daddr));
+ dprintf("> sport=%u dport=%u\n", ntohs(skb->h.uh->source), ntohs(skb->h.uh->dest));
+ dprintf("> opcode=%d vnet=%u vmac=" MACFMT " addr=" IPFMT "\n",
+ ntohs(varph->vnetmsghdr.opcode),
+ ntohl(varph->vnet),
+ MAC6TUPLE(varph->vmac.mac),
+ NIPQUAD(varph->addr));
+ varp_dprint();
+ }
+ switch(ntohs(varph->vnetmsghdr.opcode)){
+ case VARP_OP_REQUEST:
+ err = varp_handle_request(skb, varph);
+ break;
+ case VARP_OP_ANNOUNCE:
+ err = varp_handle_announce(skb, varph);
+ break;
+ default:
+ wprintf("> Unknown opcode: %d \n", ntohs(varph->vnetmsghdr.opcode));
+ break;
+ }
+ exit:
+ if(mine) err = 1;
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Send an outgoing packet on the appropriate vnet tunnel.
+ *
+ * @param skb outgoing message
+ * @param vnet vnet (host order)
+ * @return 0 on success, error code otherwise
+ */
+int varp_output(struct sk_buff *skb, u32 vnet){
+ int err = 0;
+ unsigned char *mac = NULL;
+ Vmac *vmac = NULL;
+ VarpEntry *ventry = NULL;
+
+ dprintf("> skb=%p vnet=%u\n", skb, vnet);
+ if(!varp_table){
+ err = -ENOSYS;
+ goto exit;
+ }
+ dprintf("> skb.mac=%p\n", skb->mac.raw);
+ if(!skb->mac.raw){
+ wprintf("> No ethhdr in skb!\n");
+ err = -EINVAL;
+ goto exit;
+ }
+ mac = MAC_ETH(skb)->h_dest;
+ vmac = (Vmac*)mac;
+ if(mac_is_multicast(mac)){
+ err = vnet_tunnel_send(vnet, varp_mcast_addr, skb);
+ } else {
+ ventry = VarpTable_lookup(varp_table, vnet, vmac);
+ if(!ventry){
+ ventry = VarpTable_add(varp_table, vnet, vmac);
+ }
+ if(ventry){
+ unsigned long flags;
+ VarpEntry_lock(ventry, flags);
+ err = VarpEntry_output(ventry, skb);
+ VarpEntry_unlock(ventry, flags);
+ VarpEntry_decref(ventry);
+ } else {
+ err = -ENOMEM;
+ }
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Set the varp multicast address (after initialization).
+ *
+ * @param addr address (network order)
+ * @return 0 on success, error code otherwise
+ */
+int varp_set_mcast_addr(uint32_t addr){
+ int err = 0;
+ varp_close();
+ varp_mcast_addr = addr;
+ err = varp_open(varp_mcast_addr, varp_ucast_addr, varp_port);
+ return err;
+}
+
+/** Initialize the varp multicast address from a module parameter.
+ *
+ * @param s address in IPv4 notation
+ * @return 0 on success, error code otherwise
+ */
+static void varp_init_mcast_addr(char *s){
+ unsigned long v = 0;
+
+ dprintf("> %s\n", s);
+ if(s && (get_inet_addr(s, &v) >= 0)){
+ varp_mcast_addr = (u32)v;
+ } else {
+ varp_mcast_addr = htonl(VARP_MCAST_ADDR);
+ }
+}
+
+/** Initialize the varp cache.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int varp_init(void){
+ int err = 0;
+ struct net_device *dev = NULL;
+
+ dprintf(">\n");
+ varp_table = VarpTable_new();
+ if(!varp_table){
+ err = -ENOMEM;
+ goto exit;
+ }
+ varp_init_mcast_addr(varp_mcaddr);
+ err = vnet_get_device(varp_device, &dev);
+ dprintf("> vnet_get_device(%s)=%d\n", varp_device, err);
+ if(err) goto exit;
+ err = vnet_get_device_address(dev, &varp_ucast_addr);
+ dprintf("> vnet_get_device_address()=%d\n", err);
+ if(err) goto exit;
+ varp_port = htons(VARP_PORT);
+
+ err = varp_open(varp_mcast_addr, varp_ucast_addr, varp_port);
+ dprintf("> varp_open()=%d\n", err);
+ exit:
+ if(dev) dev_put(dev);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Close the varp cache.
+ */
+void varp_exit(void){
+ dprintf(">\n");
+ varp_close();
+ if(varp_table){
+ VarpTable *z = varp_table;
+ varp_table = NULL;
+ VarpTable_free(z);
+ }
+ dprintf("<\n");
+}
+
+MODULE_PARM(varp_mcaddr, "s");
+MODULE_PARM_DESC(varp_mcaddr, "VARP multicast address");
+
+MODULE_PARM(varp_device, "s");
+MODULE_PARM_DESC(varp_device, "VARP network device");
diff --git a/tools/vnet/vnet-module/varp.h b/tools/vnet/vnet-module/varp.h
new file mode 100644
index 0000000000..4aab7fc522
--- /dev/null
+++ b/tools/vnet/vnet-module/varp.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _VNET_VARP_H
+#define _VNET_VARP_H
+
+#define CONFIG_VARP_GRATUITOUS 1
+
+struct net_device;
+struct sk_buff;
+struct Vif;
+
+#define DEVICE "xen-br0"
+
+extern int vnet_get_device(const char *name, struct net_device **dev);
+extern int vnet_get_device_address(struct net_device *dev, u32 *addr);
+
+extern int varp_handle_message(struct sk_buff *skb);
+extern int varp_output(struct sk_buff *skb, u32 vnet);
+extern int varp_update(int vnet, unsigned char *vmac, u32 addr);
+
+extern int varp_init(void);
+extern void varp_exit(void);
+
+extern int varp_open(u32 mcaddr, u32 addr, u16 port);
+extern void varp_close(void);
+extern int varp_set_mcast_addr(u32 addr);
+
+extern void varp_print(void);
+
+extern int varp_announce_vif(struct net_device *dev, struct Vif *vif);
+//extern int varp_announce_vifs(struct net_device *dev, struct task_struct *domain);
+
+extern u32 varp_mcast_addr;
+
+
+/* MAC broadcast addr is ff-ff-ff-ff-ff-ff (all 1's).
+ * MAC multicast addr has low bit 1, i.e. 01-00-00-00-00-00.
+ */
+
+/** Test if a MAC address is a multicast or broadcast address.
+ *
+ * @param mac address
+ * @return 1 if it is, 0 if not
+ */
+static inline int mac_is_multicast(u8 mac[ETH_ALEN]){
+ return mac[0] & 1;
+}
+
+/** Test if a MAC address is the broadcast address.
+ *
+ * @param mac address
+ * @return 1 if it is, 0 if not
+ */
+static inline int mac_is_broadcast(u8 mac[ETH_ALEN]){
+ u8 mac_bcast_val[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ return memcmp(mac, mac_bcast_val, ETH_ALEN) == 0;
+}
+
+/** Test if a MAC address is the all-zero address.
+ *
+ * @param mac address
+ * @return 1 if it is, 0 if not
+ */
+static inline int mac_is_zero(u8 mac[ETH_ALEN]){
+ u8 mac_zero_val[ETH_ALEN] = {};
+ return memcmp(mac, mac_zero_val, ETH_ALEN) == 0;
+}
+
+/** Print format for a mac address. */
+#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x"
+
+#define MAC6TUPLE(_mac) (_mac)[0], (_mac)[1], (_mac)[2], (_mac)[3], (_mac)[4], (_mac)[5]
+
+/** Get the subnet defined by a netmask and addr.
+ *
+ * @param netmask subnet netmask
+ * @param addr subnet address
+ * @return subnet
+ */
+static inline u32 subnet_net(u32 netmask, u32 addr){
+ return netmask & addr;
+}
+
+/** Get the address within a subnet.
+ *
+ * @param netmask subnet netmask
+ * @param addr address
+ * @return address within the subnet
+ */
+static inline u32 subnet_addr(u32 netmask, u32 addr){
+ return ~netmask & addr;
+}
+
+/** Get the broadcast address for a subnet.
+ *
+ * @param netmask subnet netmask
+ * @param netaddr subnet address
+ * @return subnet broadcast address
+ */
+static inline u32 subnet_broadcast_addr(u32 netmask, u32 netaddr){
+ return subnet_net(netmask, netaddr) | ~netmask;
+}
+
+/** Test if an address corresponds to a subnet broadcast.
+ * True if the address within the subnet is all 1's (in binary).
+ * (even if the address is not in the subnet).
+ *
+ * @param netmask subnet mask
+ * @param add address
+ * @return 1 if it does, 0 otherwise
+ */
+static inline int subnet_broadcast(u32 netmask, u32 addr){
+ return subnet_addr(netmask, INADDR_ANY) == subnet_addr(netmask, addr);
+}
+
+/** Test if an address is in a subnet.
+ *
+ * @param netmask subnet mask
+ * @param netaddr subnet address
+ * @param addr address
+ * @return 1 if it is, 0 otherwise
+ */
+static inline int subnet_local(u32 netmask, u32 netaddr, u32 addr){
+ return subnet_net(netmask, netaddr) == subnet_net(netmask, addr);
+}
+
+#endif /* ! _VNET_VARP_H */
diff --git a/tools/vnet/vnet-module/varp_socket.c b/tools/vnet/vnet-module/varp_socket.c
new file mode 100644
index 0000000000..339d42b776
--- /dev/null
+++ b/tools/vnet/vnet-module/varp_socket.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/version.h>
+
+#include <asm/uaccess.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/sched.h>
+#include <linux/file.h>
+#include <linux/version.h>
+#include <linux/smp_lock.h>
+#include <net/sock.h>
+
+#include <if_varp.h>
+#include <varp.h>
+
+/* Get macros needed to define system calls as functions in the kernel. */
+#define __KERNEL_SYSCALLS__
+static int errno;
+#include <linux/unistd.h>
+
+#define MODULE_NAME "VARP"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+// Compensate for struct sock fields having 'sk_' added
+// to them in 2.6.
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+
+#define SK_RECEIVE_QUEUE sk_receive_queue
+#define SK_SLEEP sk_sleep
+
+#else
+
+#define SK_RECEIVE_QUEUE receive_queue
+#define SK_SLEEP sleep
+
+#endif
+
+/** @file
+ * Support for the VARP udp sockets.
+ */
+
+static inline mm_segment_t change_fs(mm_segment_t fs){
+ mm_segment_t oldfs = get_fs();
+ set_fs(fs);
+ return oldfs;
+}
+
+/* Replicate the user-space socket API.
+ * The parts we need anyway.
+ */
+
+/* Define the socketcall() syscall.
+ * Multiplexes all the socket-related calls.
+ *
+ * @param call socket call id
+ * @param args arguments (upto 6)
+ * @return call-dependent value
+ */
+static inline _syscall2(int, socketcall,
+ int, call,
+ unsigned long *, args)
+
+int socket(int family, int type, int protocol){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)family;
+ args[1] = (unsigned long)type;
+ args[2] = (unsigned long)protocol;
+ return socketcall(SYS_SOCKET, args);
+}
+
+int bind(int fd, struct sockaddr *umyaddr, int addrlen){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)umyaddr;
+ args[2] = (unsigned long)addrlen;
+ return socketcall(SYS_BIND, args);
+}
+
+int connect(int fd, struct sockaddr *uservaddr, int addrlen){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)uservaddr;
+ args[2] = (unsigned long)addrlen;
+ return socketcall(SYS_CONNECT, args);
+}
+
+int sendto(int fd, void * buff, size_t len,
+ unsigned flags, struct sockaddr *addr,
+ int addr_len){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)buff;
+ args[2] = (unsigned long)len;
+ args[3] = (unsigned long)flags;
+ args[4] = (unsigned long)addr;
+ args[5] = (unsigned long)addr_len;
+ return socketcall(SYS_SENDTO, args);
+}
+
+int recvfrom(int fd, void * ubuf, size_t size,
+ unsigned flags, struct sockaddr *addr,
+ int *addr_len){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)ubuf;
+ args[2] = (unsigned long)size;
+ args[3] = (unsigned long)flags;
+ args[4] = (unsigned long)addr;
+ args[5] = (unsigned long)addr_len;
+ return socketcall(SYS_RECVFROM, args);
+}
+
+int setsockopt(int fd, int level, int optname, void *optval, int optlen){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)level;
+ args[2] = (unsigned long)optname;
+ args[3] = (unsigned long)optval;
+ args[4] = (unsigned long)optlen;
+ return socketcall(SYS_SETSOCKOPT, args);
+}
+
+int getsockopt(int fd, int level, int optname, void *optval, int *optlen){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)level;
+ args[2] = (unsigned long)optname;
+ args[3] = (unsigned long)optval;
+ args[4] = (unsigned long)optlen;
+ return socketcall(SYS_GETSOCKOPT, args);
+}
+
+int shutdown(int fd, int how){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)how;
+ return socketcall(SYS_SHUTDOWN, args);
+}
+
+int getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len){
+ unsigned long args[6];
+
+ args[0] = (unsigned long)fd;
+ args[1] = (unsigned long)usockaddr;
+ args[2] = (unsigned long)usockaddr_len;
+ return socketcall(SYS_GETSOCKNAME, args);
+}
+
+/*============================================================================*/
+/** Socket flags. */
+enum {
+ VSOCK_REUSE = 1,
+ VSOCK_BIND = 2,
+ VSOCK_CONNECT = 4,
+ VSOCK_BROADCAST = 8,
+ VSOCK_MULTICAST = 16,
+ };
+
+/** Convert socket flags to a string.
+ *
+ * @param flags flags
+ * @return static string
+ */
+char * socket_flags(int flags){
+ static char s[6];
+ int i = 0;
+ s[i++] = (flags & VSOCK_CONNECT ? 'c' : '-');
+ s[i++] = (flags & VSOCK_BIND ? 'b' : '-');
+ s[i++] = (flags & VSOCK_REUSE ? 'r' : '-');
+ s[i++] = (flags & VSOCK_BROADCAST ? 'B' : '-');
+ s[i++] = (flags & VSOCK_MULTICAST ? 'M' : '-');
+ s[i++] = '\0';
+ return s;
+}
+
+/** The varp multicast socket. */
+int varp_mcast_sock = -1;
+
+/** The varp unicast socket. */
+int varp_ucast_sock = -1;
+
+/** Control flag for whether varp should be running.
+ * If this is set 0 then the varp thread will notice and
+ * (eventually) exit. This is indicated by setting varp_running
+ * to 0.
+ */
+atomic_t varp_run = ATOMIC_INIT(0);
+
+/** State flag indicating whether the varp thread is running. */
+atomic_t varp_running = ATOMIC_INIT(0);
+
+/** Set socket option to reuse address.
+ *
+ * @param sock socket
+ * @param reuse flag
+ * @return 0 on success, error code otherwise
+ */
+int setsock_reuse(int sock, int reuse){
+ int err = 0;
+ err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+ if(err < 0){
+ eprintf("> setsockopt SO_REUSEADDR: %d %d\n", err, errno);
+ }
+ return err;
+}
+
+/** Set socket broadcast option.
+ *
+ * @param sock socket
+ * @param bcast flag
+ * @return 0 on success, error code otherwise
+ */
+int setsock_broadcast(int sock, int bcast){
+ int err = 0;
+ err = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast));
+ if(err < 0){
+ eprintf("> setsockopt SO_BROADCAST: %d %d\n", err, errno);
+ }
+ return err;
+}
+
+/** Join a socket to a multicast group.
+ *
+ * @param sock socket
+ * @param saddr multicast address
+ * @return 0 on success, error code otherwise
+ */
+int setsock_multicast(int sock, uint32_t saddr){
+ int err = 0;
+ struct net_device *dev = NULL;
+ u32 addr = 0;
+ struct ip_mreqn mreq = {};
+ int mloop = 0;
+
+ err = vnet_get_device(DEVICE, &dev);
+ if(err){
+ eprintf("> error getting device: %d %d\n", err, errno);
+ goto exit;
+ }
+ err = vnet_get_device_address(dev, &addr);
+ if(err){
+ eprintf("> error getting device address: %d %d\n", err, errno);
+ goto exit;
+ }
+ // See 'man 7 ip' for these options.
+ mreq.imr_multiaddr.s_addr = saddr; // IP multicast address.
+ //mreq.imr_address.s_addr = addr; // Interface IP address.
+ mreq.imr_address.s_addr = INADDR_ANY; // Interface IP address.
+ mreq.imr_ifindex = 0; // Interface index (0 means any).
+ dprintf("> saddr=%u.%u.%u.%u addr=%u.%u.%u.%u ifindex=%d\n",
+ NIPQUAD(saddr), NIPQUAD(addr), mreq.imr_ifindex);
+ err = setsockopt(sock, SOL_IP, IP_MULTICAST_LOOP, &mloop, sizeof(mloop));
+ if(err < 0){
+ eprintf("> setsockopt IP_MULTICAST_LOOP: %d %d\n", err, errno);
+ goto exit;
+ }
+ err = setsockopt(sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if(err < 0){
+ eprintf("> setsockopt IP_ADD_MEMBERSHIP: %d %d\n", err, errno);
+ goto exit;
+ }
+ exit:
+ err = 0; //todo: remove hack
+ return err;
+}
+
+/** Set a socket's multicast ttl (default is 1).
+ * @param sock socket
+ * @param ttl ttl
+ * @return 0 on success, error code otherwise
+ */
+int setsock_multicast_ttl(int sock, uint8_t ttl){
+ int err = 0;
+ err = setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
+ return err;
+}
+
+/** Create a socket.
+ * The flags can include VSOCK_REUSE, VSOCK_BROADCAST, VSOCK_CONNECT.
+ *
+ * @param socktype socket type
+ * @param saddr address
+ * @param port port
+ * @param flags flags
+ * @param val return value for the socket connection
+ * @return 0 on success, error code otherwise
+ */
+int create_socket(int socktype, uint32_t saddr, uint32_t port, int flags, int *val){
+ int err = 0;
+ int sock;
+ struct sockaddr_in addr_in;
+ struct sockaddr *addr = (struct sockaddr *)&addr_in;
+ int addr_n = sizeof(addr_in);
+ int reuse, bcast;
+ int sockproto = 0;
+
+ //dprintf(">\n");
+ reuse = (flags & VSOCK_REUSE);
+ bcast = (flags & VSOCK_BROADCAST);
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_addr.s_addr = saddr;
+ addr_in.sin_port = port;
+ dprintf("> flags=%s addr=%u.%u.%u.%u port=%d\n",
+ socket_flags(flags),
+ NIPQUAD(saddr), ntohs(port));
+
+ switch(socktype){
+ case SOCK_DGRAM: sockproto = IPPROTO_UDP; break;
+ case SOCK_STREAM: sockproto = IPPROTO_TCP; break;
+ }
+ sock = socket(AF_INET, socktype, sockproto);
+ if(sock < 0) goto exit;
+ if(reuse){
+ err = setsock_reuse(sock, reuse);
+ if(err < 0) goto exit;
+ }
+ if(bcast){
+ err = setsock_broadcast(sock, bcast);
+ if(err < 0) goto exit;
+ }
+ if(flags & VSOCK_MULTICAST){
+ err = setsock_multicast(sock, saddr);
+ if(err < 0) goto exit;
+ }
+ if(flags & VSOCK_CONNECT){
+ err = connect(sock, addr, addr_n);
+ if(err < 0) goto exit;
+ }
+ if(flags & VSOCK_BIND){
+ err = bind(sock, addr, addr_n);
+ if(err < 0) goto exit;
+ }
+ exit:
+ *val = (err ? -1 : sock);
+ if(err) eprintf("> err=%d errno=%d\n", err, errno);
+ return err;
+}
+
+/** Open the varp multicast socket.
+ *
+ * @param mcaddr multicast address
+ * @param saddr address
+ * @param port port
+ * @param val return parameter for the socket
+ * @return 0 on success, error code otherwise
+ */
+int varp_mcast_open(uint32_t mcaddr, uint32_t saddr, uint16_t port, int *val){
+ int err = 0;
+ int flags = VSOCK_REUSE;
+ int multicast = MULTICAST(mcaddr);
+ int sock = 0;
+ struct sockaddr_in addr_in;
+ struct sockaddr *addr = (struct sockaddr *)&addr_in;
+ int addr_n = sizeof(addr_in);
+
+ dprintf(">\n");
+ flags |= VSOCK_MULTICAST;
+ flags |= VSOCK_BROADCAST;
+
+ err = create_socket(SOCK_DGRAM, mcaddr, port, flags, &sock);
+ if(err < 0) goto exit;
+ if(multicast){
+ err = setsock_multicast_ttl(sock, 1);
+ if(err < 0) goto exit;
+ }
+ if(0){
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_addr.s_addr = saddr;
+ addr_in.sin_port = port;
+ err = bind(sock, addr, addr_n);
+ if(err < 0){
+ eprintf("> bind: %d %d\n", err, errno);
+ goto exit;
+ }
+ }
+ if(0){
+ struct sockaddr_in self = {};
+ int self_n;
+ getsockname(sock, (struct sockaddr *)&self, &self_n);
+ dprintf("> sockname sock=%d addr=%u.%u.%u.%u port=%d\n",
+ sock, NIPQUAD(saddr), ntohs(port));
+ }
+ exit:
+ if(err){
+ shutdown(sock, 2);
+ }
+ *val = (err ? -1 : sock);
+ dprintf("< err=%d val=%d\n", err, *val);
+ return err;
+}
+
+/** Open the varp unicast socket.
+ *
+ * @param addr address
+ * @param port port
+ * @param val return parameter for the socket
+ * @return 0 on success, error code otherwise
+ */
+int varp_ucast_open(uint32_t addr, u16 port, int *val){
+ int err = 0;
+ int flags = VSOCK_BIND | VSOCK_REUSE;
+ dprintf(">\n");
+ err = create_socket(SOCK_DGRAM, addr, port, flags, val);
+ dprintf("< err=%d val=%d\n", err, *val);
+ return err;
+}
+
+/* Here because inline in 'socket.c'. */
+#ifndef sockfd_put
+#define sockfd_put(sock) fput((sock)->file)
+#endif
+
+/** Get the next skb from a socket's receive queue.
+ *
+ * @param fd socket file descriptor
+ * @return skb or NULL
+ */
+static struct sk_buff *get_sock_skb(int fd){
+ int err = 0;
+ struct sk_buff *skb = NULL;
+ struct socket *sock = NULL;
+
+ sock = sockfd_lookup(fd, &err);
+ if (!sock){
+ dprintf("> no sock for fd=%d\n", fd);
+ goto exit;
+ }
+ skb = skb_dequeue(&sock->sk->SK_RECEIVE_QUEUE);
+ //skb = skb_recv_datagram(sock->sk, 0, 1, &recv_err);
+ sockfd_put(sock);
+ exit:
+ return skb;
+}
+
+/** Handle the next skb on a socket (if any).
+ *
+ * @param fd socket file descriptor
+ * @return 1 if there was an skb, 0 otherwise
+ */
+static int handle_sock_skb(int fd){
+ int ret = 0;
+ struct sk_buff *skb = get_sock_skb(fd);
+ if(skb){
+ ret = 1;
+ dprintf("> skb fd=%d skb=%p\n", fd, skb);
+ varp_handle_message(skb);
+ kfree_skb(skb);
+ }
+ return ret;
+}
+
+/** Add a wait queue to a socket.
+ *
+ * @param fd socket file descriptor
+ * @param waitq queue
+ * @return 0 on success, error code otherwise
+ */
+int sock_add_wait_queue(int fd, wait_queue_t *waitq){
+ int err = 0;
+ struct socket *sock = NULL;
+
+ dprintf("> fd=%d\n", fd);
+ sock = sockfd_lookup(fd, &err);
+ if (!sock) goto exit;
+ add_wait_queue(sock->sk->SK_SLEEP, waitq);
+ sockfd_put(sock);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Remove a wait queue from a socket.
+ *
+ * @param fd socket file descriptor
+ * @param waitq queue
+ * @return 0 on success, error code otherwise
+ */
+int sock_remove_wait_queue(int fd, wait_queue_t *waitq){
+ int err = 0;
+ struct socket *sock = NULL;
+
+ sock = sockfd_lookup(fd, &err);
+ if (!sock) goto exit;
+ remove_wait_queue(sock->sk->SK_SLEEP, waitq);
+ sockfd_put(sock);
+ exit:
+ return err;
+}
+
+/** Loop handling the varp sockets.
+ * We use kernel API for this (waitqueue, schedule_timeout) instead
+ * of select because the select syscall was returning EFAULT. Oh well.
+ *
+ * @param arg arguments
+ * @return exit code
+ */
+int varp_main(void *arg){
+ int err = 0;
+ long timeout = 3 * HZ;
+ int count = 0;
+ int n = 0;
+ DECLARE_WAITQUEUE(mcast_wait, current);
+ DECLARE_WAITQUEUE(ucast_wait, current);
+
+ dprintf("> start\n");
+ atomic_set(&varp_running, 1);
+ err = sock_add_wait_queue(varp_mcast_sock, &mcast_wait);
+ err = sock_add_wait_queue(varp_ucast_sock, &ucast_wait);
+ for(n = 1; atomic_read(&varp_run) == 1; n++){
+ //dprintf("> n=%d\n", n);
+ count = 0;
+ count += handle_sock_skb(varp_mcast_sock);
+ count += handle_sock_skb(varp_ucast_sock);
+ if(!count){
+ // No skbs were handled, so go back to sleep.
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout);
+ current->state = TASK_RUNNING;
+ }
+ }
+ sock_remove_wait_queue(varp_mcast_sock, &mcast_wait);
+ sock_remove_wait_queue(varp_ucast_sock, &ucast_wait);
+ atomic_set(&varp_running, 0);
+ //MOD_DEC_USE_COUNT;
+ dprintf("< stop err=%d\n", err);
+ return err;
+}
+
+/** Start the varp thread.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int varp_start(void){
+ int err = 0;
+ void *args = NULL;
+ int flags = 0;
+ long pid = 0;
+
+ dprintf(">\n");
+ //flags |= CLONE_VM;
+ flags |= CLONE_FS;
+ flags |= CLONE_FILES;
+ flags |= CLONE_SIGHAND;
+ atomic_set(&varp_run, 1);
+ atomic_set(&varp_running, 0);
+ pid = kernel_thread(varp_main, args, flags);
+ dprintf("< pid=%ld\n", pid);
+ return err;
+}
+
+/** Close the varp sockets and stop the thread handling them.
+ */
+void varp_close(void){
+ mm_segment_t oldfs;
+ long timeout = 1 * HZ;
+ int tries = 10;
+ dprintf(">\n");
+ // Tell the varp thread to stop and wait a while for it.
+ atomic_set(&varp_run, 0);
+ while(atomic_read(&varp_running) && tries-- > 0){
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(timeout);
+ current->state = TASK_RUNNING;
+ }
+ // Close the sockets.
+ oldfs = change_fs(KERNEL_DS);
+ if(varp_mcast_sock > 0){
+ shutdown(varp_mcast_sock, 2);
+ varp_mcast_sock = -1;
+ }
+ if(varp_ucast_sock > 0){
+ shutdown(varp_ucast_sock, 2);
+ varp_ucast_sock = -1;
+ }
+ set_fs(oldfs);
+ //MOD_DEC_USE_COUNT;
+ dprintf("<\n");
+}
+
+/** Open the varp sockets and start the thread handling them.
+ *
+ * @param mcaddr multicast address
+ * @param addr unicast address
+ * @param port port
+ * @return 0 on success, error code otherwise
+ */
+int varp_open(u32 mcaddr, u32 addr, u16 port){
+ int err = 0;
+ mm_segment_t oldfs;
+
+ //MOD_INC_USE_COUNT;
+ dprintf("> mcaddr=%u.%u.%u.%u addr=%u.%u.%u.%u port=%u\n",
+ NIPQUAD(mcaddr), NIPQUAD(addr), ntohs(port));
+ //MOD_INC_USE_COUNT;
+ oldfs = change_fs(KERNEL_DS);
+ err = varp_mcast_open(mcaddr, addr, port, &varp_mcast_sock);
+ if(err < 0 ) goto exit;
+ err = varp_ucast_open(INADDR_ANY, port, &varp_ucast_sock);
+ if(err < 0 ) goto exit;
+ set_fs(oldfs);
+ err = varp_start();
+ exit:
+ set_fs(oldfs);
+ if(err){
+ varp_close();
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
diff --git a/tools/vnet/vnet-module/vif.c b/tools/vnet/vnet-module/vif.c
new file mode 100644
index 0000000000..43d864c1a1
--- /dev/null
+++ b/tools/vnet/vnet-module/vif.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/udp.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+
+#include <etherip.h>
+#include <if_varp.h>
+#include <vnet_dev.h>
+#include <vif.h>
+#include "allocate.h"
+#include "hash_table.h"
+#include "sys_net.h"
+#include "sys_string.h"
+
+#define MODULE_NAME "VNET"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** Table of vifs indexed by VifKey. */
+HashTable *vif_table = NULL;
+
+void vif_decref(Vif *vif){
+ if(!vif) return;
+ if(atomic_dec_and_test(&vif->refcount)){
+ kfree(vif);
+ }
+}
+
+void vif_incref(Vif *vif){
+ if(!vif) return;
+ atomic_inc(&vif->refcount);
+}
+
+/** Hash function for keys in the vif table.
+ * Hashes the vnet id and mac.
+ *
+ * @param k key (VifKey)
+ * @return hashcode
+ */
+Hashcode vif_key_hash_fn(void *k){
+ VifKey *key = k;
+ Hashcode h;
+ h = hash_2ul(key->vnet,
+ (key->vmac.mac[0] << 24) |
+ (key->vmac.mac[1] << 16) |
+ (key->vmac.mac[2] << 8) |
+ (key->vmac.mac[3] ));
+ h = hash_hul(h,
+ (key->vmac.mac[4] << 8) |
+ (key->vmac.mac[5] ));
+ return h;
+}
+
+
+/** Test equality for keys in the vif table.
+ * Compares vnet and mac.
+ *
+ * @param k1 key to compare (VifKey)
+ * @param k2 key to compare (VifKey)
+ * @return 1 if equal, 0 otherwise
+ */
+int vif_key_equal_fn(void *k1, void *k2){
+ VifKey *key1 = k1;
+ VifKey *key2 = k2;
+ return (key1->vnet == key2->vnet) && (memcmp(key1->vmac.mac, key2->vmac.mac, ETH_ALEN) == 0);
+}
+
+/** Free an entry in the vif table.
+ *
+ * @param table containing table
+ * @param entry entry to free
+ */
+static void vif_entry_free_fn(HashTable *table, HTEntry *entry){
+ Vif *vif;
+ if(!entry) return;
+ vif = entry->value;
+ if(vif){
+ vif_decref(vif);
+ }
+ HTEntry_free(entry);
+}
+
+/** Lookup a vif.
+ *
+ * @param vnet vnet id
+ * @param mac MAC address
+ * @return 0 on success, -ENOENT otherwise
+ */
+int vif_lookup(int vnet, Vmac *vmac, Vif **vif){
+ int err = 0;
+ VifKey key = {};
+ HTEntry *entry = NULL;
+
+ key.vnet = vnet;
+ key.vmac = *vmac;
+ entry = HashTable_get_entry(vif_table, &key);
+ if(entry){
+ *vif = entry->value;
+ vif_incref(*vif);
+ } else {
+ *vif = NULL;
+ err = -ENOENT;
+ }
+ //dprintf("< err=%d addr=" IPFMT "\n", err, NIPQUAD(*coaddr));
+ return err;
+}
+
+/** Create a new vif.
+ *
+ * @param vnet vnet id
+ * @param mac MAC address
+ * @return 0 on success, negative error code otherwise
+ */
+int vif_add(int vnet, Vmac *vmac, Vif **val){
+ int err = 0;
+ Vif *vif = NULL;
+ HTEntry *entry;
+ dprintf("> vnet=%d\n", vnet);
+ vif = ALLOCATE(Vif);
+ if(!vif){
+ err = -ENOMEM;
+ goto exit;
+ }
+ atomic_set(&vif->refcount, 1);
+ vif->vnet = vnet;
+ vif->vmac = *vmac;
+ entry = HashTable_add(vif_table, vif, vif);
+ if(!entry){
+ err = -ENOMEM;
+ deallocate(vif);
+ vif = NULL;
+ goto exit;
+ }
+ vif_incref(vif);
+ exit:
+ *val = (err ? NULL : vif);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Delete an entry.
+ *
+ * @param vnet vnet id
+ * @param mac MAC address
+ * @param coaddr return parameter for care-of address
+ * @return number of entries deleted, or negative error code
+ */
+int vif_remove(int vnet, Vmac *vmac){
+ int err = 0;
+ VifKey key = { .vnet = vnet, .vmac = *vmac };
+ //dprintf("> vnet=%d addr=%u.%u.%u.%u\n", vnet, NIPQUAD(coaddr));
+ err = HashTable_remove(vif_table, &key);
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int vif_find(int vnet, Vmac *vmac, int create, Vif **vif){
+ int err = 0;
+
+ err = vif_lookup(vnet, vmac, vif);
+ if(err && create){
+ err = vif_add(vnet, vmac, vif);
+ }
+ return err;
+}
+
+void vif_purge(void){
+ HashTable_clear(vif_table);
+}
+
+int vif_create(int vnet, Vmac *vmac, Vif **vif){
+ int err = 0;
+
+ dprintf(">\n");
+ if(!vif_lookup(vnet, vmac, vif)){
+ err = -EEXIST;
+ goto exit;
+ }
+ dprintf("> vif_add...\n");
+ err = vif_add(vnet, vmac, vif);
+ exit:
+ if(err){
+ *vif = NULL;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Create a vif.
+ *
+ * @param vnet vnet id
+ * @param mac mac address (as a string)
+ * @return 0 on success, error code otherwise
+ */
+int mkvif(int vnet, char *mac){
+ int err = 0;
+ Vmac vmac = {};
+ Vif *vif = NULL;
+ dprintf("> vnet=%d mac=%s\n", vnet, mac);
+ err = mac_aton(mac, vmac.mac);
+ if(err) goto exit;
+ err = vif_create(vnet, &vmac, &vif);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Initialize the vif table.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int vif_init(void){
+ int err = 0;
+ dprintf(">\n");
+ vif_table = HashTable_new(0);
+ if(!vif_table){
+ err = -ENOMEM;
+ goto exit;
+ }
+ vif_table->entry_free_fn = vif_entry_free_fn;
+ vif_table->key_hash_fn = vif_key_hash_fn;
+ vif_table->key_equal_fn = vif_key_equal_fn;
+
+ // Some vifs for testing.
+ //mkvif(1, "aa:00:00:00:20:11");
+ //mkvif(2, "aa:00:00:00:20:12");
+ exit:
+ if(err < 0) wprintf("< err=%d\n", err);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+void vif_exit(void){
+ HashTable_free(vif_table);
+}
diff --git a/tools/vnet/vnet-module/vif.h b/tools/vnet/vnet-module/vif.h
new file mode 100644
index 0000000000..379725189c
--- /dev/null
+++ b/tools/vnet/vnet-module/vif.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _VNET_VIF_H_
+#define _VNET_VIF_H_
+
+#include <if_varp.h>
+struct net_device;
+
+/** Key for entries in the vif table. */
+typedef struct VifKey {
+ int vnet;
+ Vmac vmac;
+} VifKey;
+
+typedef struct Vif {
+ int vnet;
+ Vmac vmac;
+ struct net_device *dev;
+ atomic_t refcount;
+} Vif;
+
+struct HashTable;
+extern struct HashTable *vif_table;
+
+extern void vif_decref(Vif *vif);
+extern void vif_incref(Vif *vif);
+
+extern int vif_create(int vnet, Vmac *vmac, Vif **vif);
+
+extern int vif_add(int vnet, Vmac *vmac, Vif **vif);
+extern int vif_lookup(int vnet, Vmac *vmac, Vif **vif);
+extern int vif_remove(int vnet, Vmac *vmac);
+extern int vif_find(int vnet, Vmac *vmac, int create, Vif **vif);
+extern void vif_purge(void);
+
+extern int vif_init(void);
+extern void vif_exit(void);
+
+#endif
diff --git a/tools/vnet/vnet-module/vnet.c b/tools/vnet/vnet-module/vnet.c
new file mode 100644
index 0000000000..6027cd6604
--- /dev/null
+++ b/tools/vnet/vnet-module/vnet.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+
+#include <linux/string.h>
+
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+
+#include <linux/etherdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <net/checksum.h>
+
+#include <tunnel.h>
+#include <sa.h>
+#include <varp.h>
+#include <if_varp.h>
+#include <esp.h>
+#include <etherip.h>
+#include <random.h>
+#include <tunnel.h>
+
+#include <vnet_dev.h>
+#include <vnet.h>
+#include <vif.h>
+#include <vnet_ioctl.h>
+#include <sa_algorithm.h>
+
+#include "allocate.h"
+#include "hash_table.h"
+#include "sys_net.h"
+#include "sys_string.h"
+
+#define MODULE_NAME "VNET"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** Default vnet security level.
+ */
+int vnet_security_default = SA_AUTH ; //| SA_CONF;
+
+/** Key for entries in the vnet address table. */
+typedef struct VnetAddrKey {
+ /** Vnet id. */
+ int vnet;
+ /** MAC address. */
+ unsigned char mac[ETH_ALEN];
+} VnetAddrKey;
+
+/** The physical vnet. */
+Vnet *vnet_physical = NULL;
+
+/** Table of vnets indexed by id. */
+static HashTable *vnet_table = NULL;
+
+/** Decrement reference count, freeing if zero.
+ *
+ * @param info vnet (OK if null)
+ */
+void Vnet_decref(Vnet *info){
+ if(!info) return;
+ if(atomic_dec_and_test(&info->refcount)){
+ dprintf("> free vnet=%u\n", info->vnet);
+ vnet_dev_remove(info);
+ deallocate(info);
+ }
+}
+
+/** Increment reference count.
+ *
+ * @param info vnet (OK if null)
+ */
+void Vnet_incref(Vnet *info){
+ if(!info) return;
+ atomic_inc(&info->refcount);
+}
+
+/** Allocate a vnet, setting reference count to 1.
+ *
+ * @param info return parameter for vnet
+ * @return 0 on success, error code otherwise
+ */
+int Vnet_alloc(Vnet **info){
+ int err = 0;
+ *info = ALLOCATE(Vnet);
+ if(*info){
+ atomic_set(&(*info)->refcount, 1);
+ } else {
+ err = -ENOMEM;
+ }
+ return err;
+}
+
+/** Add a vnet to the table under its vnet id.
+ *
+ * @param info vnet to add
+ * @return 0 on success, error code otherwise
+ */
+int Vnet_add(Vnet *info){
+ int err = 0;
+ HTEntry *entry = NULL;
+ // Vnet_del(info->vnet); //todo: Delete existing vnet info?
+ Vnet_incref(info);
+ entry = HashTable_add(vnet_table, HKEY(info->vnet), info);
+ if(!entry){
+ err = -ENOMEM;
+ Vnet_decref(info);
+ }
+ return err;
+}
+
+/** Remove a vnet from the table.
+ *
+ * @param vnet id of vnet to remove
+ * @return number of vnets removed
+ */
+int Vnet_del(vnetid_t vnet){
+ return HashTable_remove(vnet_table, HKEY(vnet));
+}
+
+/** Lookup a vnet by id.
+ * References the vnet on success - the caller must decref.
+ *
+ * @param vnet vnet id
+ * @param info return parameter for vnet
+ * @return 0 on sucess, -ENOENT if no vnet found
+ */
+int Vnet_lookup(vnetid_t vnet, Vnet **info){
+ int err = 0;
+ dprintf("> vnet=%u info=%p\n", vnet, info);
+ dprintf("> vnet_table=%p\n",vnet_table);
+ *info = HashTable_get(vnet_table, HKEY(vnet));
+ if(*info){
+ Vnet_incref(*info);
+ } else {
+ err = -ENOENT;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Free an entry in the vnet table.
+ *
+ * @param table containing table
+ * @param entry to free
+ */
+static void vnet_entry_free_fn(HashTable *table, HTEntry *entry){
+ Vnet *info;
+ if(!entry) return;
+ info = entry->value;
+ if(info){
+ vnet_dev_remove(info);
+ Vnet_decref(info);
+ }
+ HTEntry_free(entry);
+}
+
+/** Setup some vnet entries (for testing).
+ * Vnet 1 is physical, vnets 2 to 10 are insecure, vnets above
+ * 10 are secure.
+ *
+ * @return 0 on success, negative error code otherwise
+ */
+static int vnet_setup(void){
+ int err = 0;
+ int i, n = 5; //20;
+ int security = vnet_security_default;
+ Vnet *vnet;
+
+ dprintf(">\n");
+ for(i=0; i<n; i++){
+ err = Vnet_alloc(&vnet);
+ if(err) break;
+ vnet->vnet = VNET_VIF + i;
+ vnet->security = (vnet->vnet > 10 ? security : 0);
+ //err = Vnet_add(vnet);
+ err = Vnet_create(vnet);
+ if(err) break;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Initialize the vnet table and the physical vnet.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int vnet_init(void){
+ int err = 0;
+
+ dprintf(">\n");
+ vnet_table = HashTable_new(0);
+ dprintf("> vnet_table=%p\n", vnet_table);
+ if(!vnet_table){
+ err = -ENOMEM;
+ goto exit;
+ }
+ vnet_table->entry_free_fn = vnet_entry_free_fn;
+
+ err = Vnet_alloc(&vnet_physical);
+ if(err) goto exit;
+ vnet_physical->vnet = VNET_PHYS;
+ vnet_physical->security = 0;
+ err = Vnet_add(vnet_physical);
+ if(err) goto exit;
+ err = vnet_setup();
+ if(err) goto exit;
+ err = varp_init();
+ if(err) goto exit;
+ err = vif_init();
+ exit:
+ if(err < 0) wprintf("< err=%d\n", err);
+ return err;
+}
+
+void vnet_exit(void){
+ vif_exit();
+ varp_exit();
+ HashTable_free(vnet_table);
+ vnet_table = NULL;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+
+static inline int skb_route(struct sk_buff *skb, struct rtable **prt){
+ int err = 0;
+ struct flowi fl = {
+ .oif = skb->dev->ifindex,
+ .nl_u = {
+ .ip4_u = {
+ .daddr = skb->nh.iph->daddr,
+ .saddr = skb->nh.iph->saddr,
+ .tos = skb->nh.iph->tos,
+ }
+ }
+ };
+
+ err = ip_route_output_key(prt, &fl);
+ return err;
+}
+
+#else
+
+static inline int skb_route(struct sk_buff *skb, struct rtable **prt){
+ int err = 0;
+ struct rt_key key = { };
+ key.dst = skb->nh.iph->daddr;
+ key.src = skb->nh.iph->saddr;
+ key.tos = skb->nh.iph->tos;
+ key.oif = skb->dev->ifindex;
+ err = ip_route_output_key(prt, &key);
+ return err;
+}
+
+#endif
+
+inline int skb_xmit(struct sk_buff *skb){
+ int err = 0;
+ struct rtable *rt = NULL;
+
+ dprintf("> skb=%p dev=%s\n", skb, skb->dev->name);
+
+ skb->protocol = htons(ETH_P_IP);
+ err = skb_route(skb, &rt);
+ if(err) goto exit;
+ skb->dst = &rt->u.dst;
+
+ ip_select_ident(skb->nh.iph, &rt->u.dst, NULL);
+
+ if(skb->nh.iph->saddr == 0){
+ skb->nh.iph->saddr = rt->rt_src;
+ }
+
+ skb->nh.iph->check = 0;
+ skb->nh.iph->check = ip_compute_csum(skb->nh.raw, (skb->nh.iph->ihl << 2));
+
+ err = neigh_compat_output(skb);
+
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Called when a vif sends a packet to the network.
+ * Encapsulates the packet for its vnet and forwards it.
+ *
+ * @param skb packet
+ * @return 0 on success, error code otherwise
+ *
+ * @todo fixme
+ */
+int vnet_skb_send(struct sk_buff *skb, u32 vnet){
+ int err = 0;
+ Vif *vif = NULL;
+
+ dprintf("> skb=%p vnet=%u\n", skb, vnet);
+ if(vnet == VNET_PHYS || !vnet){
+ // For completeness, send direct to the network.
+ if(skb->dev){
+ err = skb_xmit(skb);
+ } else {
+ // Can't assume eth0 - might be nbe-br or other. Need to route.
+ struct net_device *dev = NULL;
+ err = vnet_get_device(DEVICE, &dev);
+ if(err) goto exit;
+ skb->dev = dev;
+ err = skb_xmit(skb);
+ dev_put(dev);
+ }
+ } else {
+ dprintf("> varp_output\n");
+ err = varp_output(skb, vnet);
+ }
+ //dprintf("< err=%d\n", err);
+ exit:
+ if(vif) vif_decref(vif);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Receive an skb for a vnet.
+ * If the dest is broadcast, goes to all vifs on the vnet.
+ * If the dest is unicast, goes to addressed vif on vnet.
+ * For each vif we set the packet dev and receive the packet.
+ *
+ * The packet must have skb->mac.raw set and skb->data must point
+ * after the device (ethernet) header.
+ *
+ * @param skb packet
+ * @param vnet packet vnet
+ * @param vmac packet vmac
+ * @return 0 on success, error code otherwise
+ */
+#if 1
+int vnet_skb_recv(struct sk_buff *skb, u32 vnet, Vmac *vmac){
+ // Receive the skb for a vnet.
+ // We make the skb come out of the vif for the vnet, and
+ // let ethernet bridging forward it to related interfaces.
+ int err = 0;
+ Vnet *info = NULL;
+
+ dprintf("> vnet=%u mac=%s\n", vnet, mac_ntoa(vmac->mac));
+ err = Vnet_lookup(vnet, &info);
+ if(err) goto exit;
+ skb->dev = info->dev;
+ dprintf("> netif_rx dev=%s\n", skb->dev->name);
+ netif_rx(skb);
+ exit:
+ if(info) Vnet_decref(info);
+ if(err){
+ kfree_skb(skb);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+#else
+int vnet_skb_recv(struct sk_buff *skb, u32 vnet, Vmac *vmac){
+ int err = 0;
+ Vif *vif = NULL;
+
+ dprintf("> vnet=%u mac=%s\n", vnet, mac_ntoa(vmac->mac));
+ if(mac_is_multicast(vmac->mac)){
+ HashTable_for_decl(entry);
+ int count = 0;
+ struct sk_buff *new_skb;
+
+ HashTable_for_each(entry, vif_table){
+ vif = entry->value;
+ if(vif->vnet != vnet) continue;
+ count++;
+ new_skb = skb_copy(skb, GFP_ATOMIC);
+ if(!new_skb) break;
+ new_skb->dev = vif->dev;
+ dprintf("> %d] netif_rx dev=%s\n", count, new_skb->dev->name);
+ netif_rx(new_skb);
+ }
+ kfree_skb(skb);
+ } else {
+ err = vif_lookup(vnet, vmac, &vif);
+ if(err){
+ kfree_skb(skb);
+ goto exit;
+ }
+ skb->dev = vif->dev;
+ dprintf("> netif_rx dev=%s\n", skb->dev->name);
+ netif_rx(skb);
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+#endif
+
+/** Check validity of an incoming IP frame.
+ *
+ * @param skb frame
+ * @return 0 if ok, error code otherwise
+ *
+ * @todo fixme Can prob skip most of this because linux will have done it.
+ * @todo Only need the vnet skb context check.
+ */
+int check_ip_frame(struct sk_buff *skb){
+ int err = -EINVAL;
+ struct iphdr* iph;
+ struct net_device *dev;
+ __u32 len;
+ __u16 check;
+
+#if 0
+ if(skb->context){
+ // Todo: After ESP want to skip most checks (including checksum),
+ // Todo: but in general may not want to skip all checks on detunnel.
+ //dprintf("> Skip check, has context\n");
+ err = 0;
+ goto exit;
+ }
+#endif
+ // Check we have enough for an ip header - the skb passed should
+ // have data pointing at the eth header and skb->len should include
+ // that. skb->nh should already have been set. Let the indvidual
+ // protocol handlers worry about the exact ip header len
+ // (i.e. whether any ip options are set).
+ dev = skb->dev;
+
+ if(skb->len < ETH_HLEN + sizeof(struct iphdr)){
+ wprintf("> packet too short for ip header\n");
+ goto exit;
+ }
+
+ iph = skb->nh.iph;
+ /*
+ * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
+ *
+ * Is the datagram acceptable?
+ *
+ * 1. Length at least the size of an ip header
+ * 2. Version of 4
+ * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
+ * 4. Doesn't have a bogus length
+ */
+ if (iph->ihl < 5 || iph->version != 4){
+ wprintf("> len and version check failed\n");
+ goto exit;
+ }
+ if(skb->len < ETH_HLEN + (iph->ihl << 2)){
+ wprintf("> packet too short for given ihl\n");
+ goto exit;
+ }
+
+ check = iph->check;
+ //iph->check = 0;
+ //iph->check = compute_cksum((__u16 *)iph, (iph->ihl << 1));
+ if(iph->check != check){
+ wprintf("> invalid checksum\n");
+ goto exit;
+ }
+
+ len = ntohs(iph->tot_len);
+ if (skb->len < len + ETH_HLEN || len < (iph->ihl << 2)){
+ wprintf("> packet too short for tot_len\n");
+ goto exit;
+ }
+ skb->h.raw = skb->nh.raw + (iph->ihl << 2);
+ err = 0;
+ exit:
+ return err;
+}
+
+/** Determine ESP security mode for a new SA.
+ *
+ * @param spi incoming spi
+ * @param protocol incoming protocol
+ * @param addr source address
+ * @return security level or negative error code
+ *
+ * @todo Need to check spi, and do some lookup for security params.
+ */
+int vnet_sa_security(u32 spi, int protocol, u32 addr){
+ int security = vnet_security_default;
+ dprintf("< security=%x\n", security);
+ return security;
+}
+
+/** Create a new SA for incoming traffic.
+ *
+ * @param spi incoming spi
+ * @param protocol incoming protocol
+ * @param addr source address
+ * @param sa return parameter for SA
+ * @return 0 on success, error code otherwise
+ */
+int vnet_sa_create(u32 spi, int protocol, u32 addr, SAState **sa){
+ int err = 0;
+ int security = vnet_sa_security(spi, protocol, addr);
+ if(security < 0){
+ err = security;
+ goto exit;
+ }
+ err = sa_create(security, spi, protocol, addr, sa);
+ exit:
+ return err;
+}
+
+/** Check that a context has the correct properties w.r.t. a vnet.
+ * The context must be secure if the vnet requires security.
+ *
+ * @param vnet vnet id
+ * @param context context
+ * @return 0 on success, error code otherwise
+ *
+ * @todo Need to check that the sa provides the correct security level.
+ */
+int vnet_check_context(int vnet, SkbContext *context, Vnet **val){
+ int err = 0;
+ Vnet *info = NULL;
+ SAState *sa = NULL;
+
+ err = Vnet_lookup(vnet, &info);
+ if(err){
+ wprintf("> No vnet %d\n", vnet);
+ goto exit;
+ }
+ if(!info->security) goto exit;
+ err = -EINVAL;
+ if(!context){
+ wprintf("> No security context\n");
+ goto exit;
+ }
+ if(context->protocol != IPPROTO_ESP){
+ wprintf("> Invalid protocol: wanted %d, got %d\n", IPPROTO_ESP, context->protocol);
+ goto exit;
+ }
+ sa = context->data;
+ //todo: Check security properties of the SA are correct w.r.t. the vnet.
+ //Something like sa->security == info->security;
+ err = 0;
+ exit:
+ *val = info;
+ return err;
+}
+
+/** Open function for SA tunnels.
+ *
+ * @param tunnel to open
+ * @return 0 on success, error code otherwise
+ */
+static int sa_tunnel_open(Tunnel *tunnel){
+ int err = 0;
+ //dprintf(">\n");
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Close function for SA tunnels.
+ *
+ * @param tunnel to close (OK if null)
+ */
+static void sa_tunnel_close(Tunnel *tunnel){
+ SAState *sa;
+ dprintf(">\n");
+ if(!tunnel) return;
+ sa = tunnel->data;
+ if(!sa) return;
+ SAState_decref(sa);
+ tunnel->data = NULL;
+ dprintf("<\n");
+}
+
+/** Packet send function for SA tunnels.
+ *
+ * @param tunnel to send on
+ * @param skb packet to send
+ * @return 0 on success, negative error code on error
+ */
+static int sa_tunnel_send(Tunnel *tunnel, struct sk_buff *skb){
+ int err = -EINVAL;
+ SAState *sa;
+ //dprintf("> tunnel=%p\n", tunnel);
+ if(!tunnel){
+ wprintf("> Null tunnel!\n");
+ goto exit;
+ }
+ sa = tunnel->data;
+ if(!sa){
+ wprintf("> Null SA!\n");
+ goto exit;
+ }
+ err = SAState_send(sa, skb, tunnel->base);
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Functions used by SA tunnels. */
+static TunnelType _sa_tunnel_type = {
+ .name = "SA",
+ .open = sa_tunnel_open,
+ .close = sa_tunnel_close,
+ .send = sa_tunnel_send
+};
+
+/** Functions used by SA tunnels. */
+TunnelType *sa_tunnel_type = &_sa_tunnel_type;
+
+/** Open a tunnel for a vnet to a given address.
+ *
+ * @param vnet vnet id
+ * @param addr destination address
+ * @param tunnel return parameter
+ * @return 0 on success, error code otherwise
+ */
+int vnet_tunnel_open(u32 vnet, u32 addr, Tunnel **tunnel){
+ extern TunnelType *etherip_tunnel_type;
+ int err = 0;
+ Vnet *info = NULL;
+ Tunnel *base_tunnel = NULL;
+ Tunnel *sa_tunnel = NULL;
+ Tunnel *etherip_tunnel = NULL;
+
+ dprintf("> vnet=%u addr=" IPFMT "\n", vnet, NIPQUAD(addr));
+ err = Vnet_lookup(vnet, &info);
+ dprintf("> Vnet_lookup=%d\n", err);
+ if(err) goto exit;
+ if(info->security){
+ SAState *sa = NULL;
+ dprintf("> security=%d\n", info->security);
+ err = Tunnel_create(sa_tunnel_type, vnet, addr, base_tunnel, &sa_tunnel);
+ if(err) goto exit;
+ dprintf("> sa_tunnel=%p\n", sa_tunnel);
+ err = sa_create(info->security, 0, IPPROTO_ESP, addr, &sa);
+ if(err) goto exit;
+ sa_tunnel->data = sa;
+ dprintf("> sa=%p\n", sa);
+ base_tunnel = sa_tunnel;
+ }
+ err = Tunnel_create(etherip_tunnel_type, vnet, addr, base_tunnel, &etherip_tunnel);
+ if(err) goto exit;
+ err = Tunnel_add(etherip_tunnel);
+ exit:
+ Tunnel_decref(sa_tunnel);
+ Vnet_decref(info);
+ if(err){
+ *tunnel = NULL;
+ } else {
+ *tunnel = etherip_tunnel;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Lookup a tunnel for a vnet to a given address.
+ * Uses an existing tunnel if there is one.
+ *
+ * @param vnet vnet id
+ * @param addr care-of address
+ * @param tunnel return parameter
+ * @return 0 on success, error code otherwise
+ */
+int vnet_tunnel_lookup(u32 vnet, u32 addr, Tunnel **tunnel){
+ int err = 0;
+ dprintf("> vnet=%d addr=" IPFMT "\n", vnet, NIPQUAD(addr));
+ *tunnel = Tunnel_lookup(vnet, addr);
+ if(!*tunnel){
+ err = vnet_tunnel_open(vnet, addr, tunnel);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Send a packet on the appropriate tunnel.
+ *
+ * @param vnet vnet
+ * @param addr tunnel endpoint
+ * @param skb packet
+ * @return 0 on success, error code otherwise
+ */
+int vnet_tunnel_send(vnetid_t vnet, vnetaddr_t addr, struct sk_buff *skb){
+ int err = 0;
+ Tunnel *tunnel = NULL;
+ dprintf("> vnet=%u addr=" IPFMT "\n", vnet, NIPQUAD(addr));
+ err = vnet_tunnel_lookup(vnet, addr, &tunnel);
+ if(err) goto exit;
+ err = Tunnel_send(tunnel, skb);
+ Tunnel_decref(tunnel);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+static void __exit vnet_module_exit(void){
+ ProcFS_exit();
+ sa_table_exit();
+ vnet_exit();
+ esp_module_exit();
+ etherip_module_exit();
+ tunnel_module_init();
+ random_module_exit();
+}
+
+/** Initialize the vnet module.
+ * Failure is fatal.
+ *
+ * @return 0 on success, error code otherwise
+ */
+static int __init vnet_module_init(void){
+ int err = 0;
+
+ dprintf(">\n");
+ err = random_module_init();
+ if(err) wprintf("> random_module_init err=%d\n", err);
+ if(err) goto exit;
+ err = tunnel_module_init();
+ if(err) wprintf("> tunnel_module_init err=%d\n", err);
+ if(err) goto exit;
+ err = etherip_module_init();
+ if(err) wprintf("> etherip_module_init err=%d\n", err);
+ if(err) goto exit;
+ err = esp_module_init();
+ if(err) wprintf("> esp_module_init err=%d\n", err);
+ if(err) goto exit;
+ err = vnet_init();
+ if(err) wprintf("> vnet_init err=%d\n", err);
+ if(err) goto exit;
+ sa_algorithm_probe_all();
+ err = sa_table_init();
+ if(err) wprintf("> sa_table_init err=%d\n", err);
+ ProcFS_init();
+ exit:
+ if(err < 0){
+ vnet_module_exit();
+ }
+ if(err < 0) wprintf("< err=%d\n", err);
+ return err;
+}
+
+module_init(vnet_module_init);
+module_exit(vnet_module_exit);
+MODULE_LICENSE("GPL");
diff --git a/tools/vnet/vnet-module/vnet.h b/tools/vnet/vnet-module/vnet.h
new file mode 100644
index 0000000000..3cee13bbd7
--- /dev/null
+++ b/tools/vnet/vnet-module/vnet.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef __VNET_VNET_H__
+#define __VNET_VNET_H__
+
+#include <asm/atomic.h>
+#include <linux/skbuff.h>
+
+#include <tunnel.h>
+#include <skb_context.h>
+
+struct Vmac;
+struct Vif;
+struct net_device;
+
+typedef uint32_t vnetid_t;
+typedef uint32_t vnetaddr_t;
+
+/** Vnet property record. */
+typedef struct Vnet {
+ /** Reference count. */
+ atomic_t refcount;
+ /** Vnet id. */
+ vnetid_t vnet;
+ /** Security flag. If true the vnet requires ESP. */
+ int security;
+
+ struct net_device *dev;
+ struct net_device *bridge;
+
+ /** Max size of the header. */
+ int header_n;
+ /** Statistics. */
+ struct net_device_stats stats;
+ int recursion;
+} Vnet;
+
+extern int Vnet_lookup(vnetid_t id, Vnet **vnet);
+extern int Vnet_add(Vnet *vnet);
+extern int Vnet_del(vnetid_t vnet);
+extern void Vnet_incref(Vnet *);
+extern void Vnet_decref(Vnet *);
+extern int Vnet_alloc(Vnet **vnet);
+extern Vnet *vnet_physical;
+
+extern int skb_xmit(struct sk_buff *skb);
+extern int vnet_skb_send(struct sk_buff *skb, u32 vnet);
+extern int vnet_skb_recv(struct sk_buff *skb, u32 vnet, struct Vmac *vmac);
+
+extern int vnet_check_context(int vnet, SkbContext *context, Vnet **vinfo);
+
+extern int vnet_tunnel_open(vnetid_t vnet, vnetaddr_t addr, Tunnel **tunnel);
+extern int vnet_tunnel_lookup(vnetid_t vnet, vnetaddr_t addr, Tunnel **tunnel);
+extern int vnet_tunnel_send(vnetid_t vnet, vnetaddr_t addr, struct sk_buff *skb);
+
+extern int vnet_init(void);
+
+enum {
+ HANDLE_OK = 1,
+ HANDLE_NO = 0,
+};
+
+extern int vnet_sa_security(u32 spi, int protocol, u32 addr);
+struct SAState;
+extern int vnet_sa_create(u32 spi, int protocol, u32 addr, struct SAState **sa);
+
+enum {
+ VNET_PHYS = 1,
+ VNET_VIF = 2,
+};
+
+#endif /* !__VNET_VNET_H__ */
diff --git a/tools/vnet/vnet-module/vnet_dev.c b/tools/vnet/vnet-module/vnet_dev.c
new file mode 100644
index 0000000000..3836606b4f
--- /dev/null
+++ b/tools/vnet/vnet-module/vnet_dev.c
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/in.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+
+#include <linux/if_arp.h>
+#include <linux/in6.h>
+#include <linux/inetdevice.h>
+#include <linux/arcdevice.h>
+#include <linux/if_bridge.h>
+
+#include <etherip.h>
+#include <vnet.h>
+#include <varp.h>
+#include <vif.h>
+#include <vnet_dev.h>
+
+#define MODULE_NAME "VNET"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+#define VNETIF_FMT "vnetif%u"
+#define VNETBR_FMT "vnet%u"
+
+#ifndef CONFIG_BRIDGE
+#error Must configure ethernet bridging in Network Options
+#endif
+
+#include <linux/../../net/bridge/br_private.h>
+#define dev_bridge(_dev) ((struct net_bridge *)(_dev)->priv)
+
+static void vnet_dev_destructor(struct net_device *dev){
+ dprintf(">\n");
+ dev->open = NULL;
+ dev->stop = NULL;
+ dev->uninit = NULL;
+ dev->destructor = NULL;
+ dev->hard_start_xmit = NULL;
+ dev->get_stats = NULL;
+ dev->do_ioctl = NULL;
+ dev->change_mtu = NULL;
+
+ dev->tx_timeout = NULL;
+ dev->set_multicast_list = NULL;
+ dev->flags = 0;
+
+ dev->priv = NULL;
+}
+
+static void vnet_dev_uninit(struct net_device *dev){
+ //Vnet *vnet = dev->priv;
+ dprintf(">\n");
+ //dev_put(dev);
+ dprintf("<\n");
+}
+
+static struct net_device_stats *vnet_dev_get_stats(struct net_device *dev){
+ Vnet *vnet = dev->priv;
+ //dprintf(">\n");
+ return &vnet->stats;
+}
+
+static int vnet_dev_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd){
+ int err = 0;
+
+ dprintf(">\n");
+ return err;
+}
+
+static int vnet_dev_change_mtu(struct net_device *dev, int mtu){
+ int err = 0;
+ Vnet *vnet = dev->priv;
+ if (mtu < 68 || mtu > 1500 - vnet->header_n){
+ err = -EINVAL;
+ goto exit;
+ }
+ dev->mtu = mtu;
+ exit:
+ return err;
+}
+
+static int vnet_dev_set_name(struct net_device *dev){
+ int err = 0;
+ Vnet *vnet = (void*)dev->priv;
+
+ dprintf(">\n");
+ dprintf("> vnet=%d\n", vnet->vnet);
+ snprintf(dev->name, IFNAMSIZ - 1, VNETIF_FMT, vnet->vnet);
+ if(__dev_get_by_name(dev->name)){
+ err = -ENOMEM;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+//============================================================================
+#ifdef CONFIG_VNET_BRIDGE
+
+#define BRIDGE DEVICE
+
+void vnet_bridge_fini(Vnet *vnet){
+ if(!vnet) return;
+ if(vnet->bridge){
+ br_del_bridge(vnet->bridge->name);
+ vnet->bridge = NULL;
+ }
+}
+
+/** Create the bridge for a vnet, and add the
+ * vnet interface to it.
+ *
+ * @param vnet vnet
+ * @return 0 on success, error code otherwise
+ */
+int vnet_bridge_init(Vnet *vnet){
+ int err = 0;
+ char bridge[IFNAMSIZ] = {};
+ struct net_bridge *br;
+ vnet->bridge = NULL;
+ snprintf(bridge, IFNAMSIZ - 1, VNETBR_FMT, vnet->vnet);
+ rtnl_lock();
+ err = br_add_bridge(bridge);
+ rtnl_unlock();
+ if(err){
+ dprintf("> Error creating vnet bridge %s: err=%d\n", bridge, err);
+ goto exit;
+ }
+ vnet->bridge = __dev_get_by_name(bridge);
+ if(!vnet->bridge){
+ wprintf("> Vnet bridge %s is null!\n", bridge);
+ err = -EINVAL;
+ goto exit;
+ }
+ br = dev_bridge(vnet->bridge);
+ br->stp_enabled = 0;
+ br->bridge_hello_time = 0;
+ br->hello_time = 0;
+ br->bridge_forward_delay = 0;
+ br->forward_delay = 0;
+ rtnl_lock();
+ err = br_add_if(br, vnet->dev);
+ rtnl_unlock();
+ if(err){
+ dprintf("> Error adding vif %s to vnet bridge %s: err=%d\n",
+ vnet->dev->name, bridge, err);
+ goto exit;
+ }
+ rtnl_lock();
+ dev_open(vnet->dev);
+ dev_open(vnet->bridge);
+ rtnl_unlock();
+ exit:
+ if(err){
+ if(vnet->bridge){
+ rtnl_lock();
+ br_del_bridge(bridge);
+ rtnl_unlock();
+ vnet->bridge = NULL;
+ }
+ }
+ return err;
+}
+
+
+/** Add an interface to the bridge for a vnet.
+ *
+ * @param vnet vnet
+ * @param dev interface
+ * @return 0 on success, error code otherwise
+ */
+int vnet_add_if(Vnet *vnet, struct net_device *dev){
+ int err = 0;
+ struct net_device *brdev;
+
+ dprintf(">\n");
+ if(!vnet->bridge){
+ err = -EINVAL;
+ goto exit;
+ }
+ // Delete the interface from the default bridge.
+ // todo: Really want to delete it from any bridge it's in.
+ if(!vnet_get_device(BRIDGE, &brdev)){
+ rtnl_lock();
+ br_del_if(dev_bridge(brdev), dev);
+ rtnl_unlock();
+ }
+ dprintf("> br_add_if %s %s\n", vnet->bridge->name, dev->name);
+ rtnl_lock();
+ dev_open(dev);
+ dev_open(vnet->bridge);
+ err = br_add_if(dev_bridge(vnet->bridge), dev);
+ rtnl_unlock();
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int vnet_del_if(Vnet *vnet, struct net_device *dev){
+ int err = 0;
+
+ dprintf(">\n");
+ if(!vnet->bridge){
+ err = -EINVAL;
+ goto exit;
+ }
+ rtnl_lock();
+ br_del_if(dev_bridge(vnet->bridge), dev);
+ rtnl_unlock();
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+
+/** Create the bridge and virtual interface for a vnet.
+ *
+ * @param info vnet
+ * @return 0 on success, error code otherwise
+ */
+int Vnet_create(Vnet *info){
+ int err = 0;
+
+ dprintf("> %u\n", info->vnet);
+ err = vnet_dev_add(info);
+ if(err) goto exit;
+ dprintf("> vnet_bridge_init\n");
+ err = vnet_bridge_init(info);
+ if(err) goto exit;
+ dprintf("> Vnet_add...\n");
+ err = Vnet_add(info);
+ exit:
+ if(err){
+ dprintf("> vnet_bridge_fini...\n");
+ vnet_bridge_fini(info);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+
+
+/** Remove the net device for a vnet.
+ * Clears the dev field of the vnet.
+ * Safe to call if the vnet or its dev are null.
+ *
+ * @param vnet vnet
+ */
+void vnet_dev_remove(Vnet *vnet){
+ if(!vnet) return;
+ dprintf("> vnet=%u\n", vnet->vnet);
+ if(vnet->bridge){
+ dprintf("> br_del_bridge(%s)\n", vnet->bridge->name);
+ rtnl_lock();
+ br_del_bridge(vnet->bridge->name);
+ rtnl_unlock();
+ vnet->bridge = NULL;
+ }
+ if(vnet->dev){
+ //dev_put(vnet->dev);
+ dprintf("> unregister_netdev(%s)\n", vnet->dev->name);
+ unregister_netdev(vnet->dev);
+ vnet->dev = NULL;
+ }
+ dprintf("<\n");
+}
+
+//============================================================================
+#else
+//============================================================================
+
+/** Create the virtual interface for a vnet.
+ *
+ * @param info vnet
+ * @return 0 on success, error code otherwise
+ */
+int Vnet_create(Vnet *info){
+ int err = 0;
+
+ dprintf("> %u\n", info->vnet);
+ err = vnet_dev_add(info);
+ if(err) goto exit;
+ dprintf("> Vnet_add...\n");
+ err = Vnet_add(info);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+int vnet_add_if(Vnet *vnet, struct net_device *dev){
+ int err = -ENOSYS;
+ return err;
+}
+
+
+int vnet_del_if(Vnet *vnet, struct net_device *dev){
+ int err = 0;
+ return err;
+}
+
+/** Remove the net device for a vnet.
+ * Clears the dev field of the vnet.
+ * Safe to call if the vnet or its dev are null.
+ *
+ * @param vnet vnet
+ */
+void vnet_dev_remove(Vnet *vnet){
+ if(!vnet) return;
+ dprintf("> vnet=%u\n", vnet->vnet);
+ if(vnet->dev){
+ //dev_put(vnet->dev);
+ dprintf("> unregister_netdev(%s)\n", vnet->dev->name);
+ unregister_netdev(vnet->dev);
+ vnet->dev = NULL;
+ }
+ dprintf("<\n");
+}
+#endif
+//============================================================================
+
+static int vnet_dev_open(struct net_device *dev){
+ int err = 0;
+ dprintf(">\n");
+ netif_start_queue(dev);
+ dprintf("<\n");
+ return err;
+}
+
+static int vnet_dev_stop(struct net_device *dev){
+ int err = 0;
+ dprintf(">\n");
+ netif_stop_queue(dev);
+ dprintf("<\n");
+ return err;
+}
+
+static int vnet_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev){
+ int err = 0;
+ Vnet *vnet = dev->priv;
+
+ dprintf("> skb=%p\n", skb);
+ if(vnet->recursion++) {
+ vnet->stats.collisions++;
+ vnet->stats.tx_errors++;
+ wprintf("> recursion!\n");
+ dev_kfree_skb(skb);
+ goto exit;
+ }
+ if(!skb){
+ err = -EINVAL;
+ wprintf("> skb NULL!\n");
+ goto exit;
+ }
+ dprintf("> skb->data=%p skb->mac.raw=%p\n", skb->data, skb->mac.raw);
+ if(skb->mac.raw < skb->data || skb->mac.raw > skb->nh.raw){
+ wprintf("> skb mac duff!\n");
+ skb->mac.raw = skb->data;
+ }
+ //dev->trans_start = jiffies;
+ err = vnet_skb_send(skb, vnet->vnet);
+ if(err < 0){
+ vnet->stats.tx_errors++;
+ } else {
+ vnet->stats.tx_packets++;
+ vnet->stats.tx_bytes += skb->len;
+ }
+ exit:
+ vnet->recursion--;
+ dprintf("<\n");
+ return 0;
+}
+
+void vnet_dev_tx_timeout(struct net_device *dev){
+ dprintf(">\n");
+ //dev->trans_start = jiffies;
+ //netif_wake_queue(dev);
+}
+
+void vnet_dev_set_multicast_list(struct net_device *dev){
+ dprintf(">\n");
+}
+
+static int (*eth_hard_header)(struct sk_buff *skb,
+ struct net_device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len) = NULL;
+
+static int vnet_dev_hard_header(struct sk_buff *skb,
+ struct net_device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len){
+ int err = 0;
+ dprintf("> skb=%p ethhdr=%p dev=%s len=%u\n",
+ skb, skb->mac.raw, dev->name, len);
+ if(saddr){
+ dprintf("> saddr=" MACFMT "\n", MAC6TUPLE((unsigned char*)saddr));
+ } else {
+ dprintf("> saddr=NULL\n");
+ }
+ if(daddr){
+ dprintf("> daddr=" MACFMT "\n", MAC6TUPLE((unsigned char*)daddr));
+ } else {
+ dprintf("> daddr=NULL\n");
+ }
+ err = eth_hard_header(skb, dev, type, daddr, saddr, len);
+ dprintf("> eth_hard_header=%d\n", err);
+ skb->mac.raw = skb->data;
+ dprintf("> src=" MACFMT " dst=" MACFMT "\n",
+ MAC6TUPLE(skb->mac.ethernet->h_source),
+ MAC6TUPLE(skb->mac.ethernet->h_dest));
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+void vnet_dev_mac(unsigned char *mac){
+ static unsigned val = 1;
+ struct net_device *dev;
+
+ if(vnet_get_device(DEVICE, &dev)){
+ mac[0] = 0xAA;
+ mac[1] = 0xFF;
+ mac[2] = (unsigned char)((val >> 24) & 0xff);
+ mac[3] = (unsigned char)((val >> 16) & 0xff);
+ mac[4] = (unsigned char)((val >> 8) & 0xff);
+ mac[5] = (unsigned char)((val ) & 0xff);
+ val++;
+ } else {
+ memcpy(mac, dev->dev_addr, ETH_ALEN);
+ dev_put(dev);
+ }
+}
+
+static int vnet_dev_init(struct net_device *dev){
+ int err = 0;
+ Vnet *vnet = (void*)dev->priv;
+
+ dprintf(">\n");
+ ether_setup(dev);
+
+ if(!eth_hard_header) eth_hard_header = dev->hard_header;
+ dev->hard_header = vnet_dev_hard_header;
+
+ dev->open = vnet_dev_open;
+ dev->stop = vnet_dev_stop;
+ dev->uninit = vnet_dev_uninit;
+ dev->destructor = vnet_dev_destructor;
+ dev->hard_start_xmit = vnet_dev_hard_start_xmit;
+ dev->get_stats = vnet_dev_get_stats;
+ dev->do_ioctl = vnet_dev_do_ioctl;
+ dev->change_mtu = vnet_dev_change_mtu;
+
+ dev->tx_timeout = vnet_dev_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+ dev->set_multicast_list = vnet_dev_set_multicast_list;
+
+ dev->hard_header_len += vnet->header_n;
+ dev->mtu -= vnet->header_n;
+
+ vnet_dev_mac(dev->dev_addr);
+
+ dev->flags |= IFF_DEBUG;
+ dev->flags |= IFF_PROMISC;
+ dev->flags |= IFF_ALLMULTI;
+
+ dprintf("<\n");
+ return err;
+}
+
+/** Add the interface (net device) for a vnet.
+ * Sets the dev field of the vnet on success.
+ * Does nothing if the vif already has an interface.
+ *
+ * @param vif vif
+ * @return 0 on success, error code otherwise
+ */
+int vnet_dev_add(Vnet *vnet){
+ int err = 0;
+ struct net_device *dev = NULL;
+
+ dprintf("> vnet=%p\n", vnet);
+ if(vnet->dev) goto exit;
+ vnet->header_n = sizeof(struct iphdr) + sizeof(struct etheriphdr);
+ dev = kmalloc(sizeof(struct net_device), GFP_ATOMIC);
+ if(!dev){ err = -ENOMEM; goto exit; }
+ *dev = (struct net_device){};
+ dev->priv = vnet;
+ vnet->dev = dev;
+
+ err = vnet_dev_set_name(dev);
+ if(err) goto exit;
+ vnet_dev_init(dev);
+ dprintf("> name=%s, register_netdev...\n", dev->name);
+ err = register_netdev(dev);
+ dprintf("> register_netdev=%d\n", err);
+ if(err) goto exit;
+ rtnl_lock();
+ dev_open(dev);
+ rtnl_unlock();
+
+ //dev_hold(dev);
+ exit:
+ if(err){
+ if(dev) kfree(dev);
+ vnet->dev = NULL;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
diff --git a/tools/vnet/vnet-module/vnet_dev.h b/tools/vnet/vnet-module/vnet_dev.h
new file mode 100644
index 0000000000..168f50c91b
--- /dev/null
+++ b/tools/vnet/vnet-module/vnet_dev.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _VNET_VNET_DEV_H_
+#define _VNET_VNET_DEV_H_
+
+struct Vnet;
+struct net_device;
+
+extern int vnet_dev_add(struct Vnet *vnet);
+extern void vnet_dev_remove(struct Vnet *vnet);
+extern int Vnet_create(struct Vnet *info);
+extern int vnet_add_if(struct Vnet *vnet, struct net_device *dev);
+extern int vnet_del_if(struct Vnet *vnet, struct net_device *dev);
+
+#endif
diff --git a/tools/vnet/vnet-module/vnet_ioctl.c b/tools/vnet/vnet-module/vnet_ioctl.c
new file mode 100644
index 0000000000..5f9f16b2e6
--- /dev/null
+++ b/tools/vnet/vnet-module/vnet_ioctl.c
@@ -0,0 +1,815 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
+#include <asm/uaccess.h>
+
+#include <linux/slab.h>
+
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+
+#include <sa.h>
+#include "vif.h"
+#include "vnet.h"
+#include "varp.h"
+#include "vnet_dev.h"
+
+#include "sxpr_parser.h"
+#include "iostream.h"
+#include "kernel_stream.h"
+#include "sys_string.h"
+#include "sys_net.h"
+
+#define MODULE_NAME "VNET"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+// Functions to manage vnets.
+/*
+
+Have to rely on ethernet bridging being configured - but we can't rely
+on the kernel interface being available to us (it's not exported @!$"%!).
+
+Create a vnet N:
+- create the vnet device vnetifN: using commands to /proc, kernel api
+- create the vnet bridge vnetN: using brctl in user-space
+- for best results something should keep track of the mapping vnet id <-> bridge name
+
+Add vif device vifD.N to vnet N.
+- domain is configured with vifD.N on bridge vnetN
+- vif script adds vif to bridge using brctl
+- vif script detects that the bridge is a vnet bridge and
+ uses /proc commands to configure the mac on the vnet
+
+Wouldn't be hard to add support for specifying vnet keys(s) in
+the control interface.
+
+*/
+
+ // id vnet id
+ // security security level
+ // ciphersuite: digest, cipher, keys??
+/* Security policy.
+ vnet
+ src: mac
+ dst: mac
+ coa: ip
+ Map vnet x coa -> security (none, auth, conf)
+
+ Policy, e.g.
+ - same subnet x vnet
+ - diff subnet x vnet
+ - some subnet x vnet
+ - some host addr x vnet
+
+ (security (net local) (vnet *) (mode none))
+ (security (net (not local))
+
+ (security (addr, vnet) (local-subnet addr) none)
+ (security (addr, vnet) (not (local-subnet addr)) conf)
+ (security (addr, vnet) (host 15.144.27.80)
+ (security (addr, vnet) (subnet addr 15.144.24.0/24) auth)
+ (security (addr, vnet) t auth)
+
+ (security (addr local) (mode none))
+ (security (addr local/16) (mode none))
+ (security (addr 15.144.0.0/16) (mode auth))
+ (security (addr 15.0.0.0/8) (mode conf))
+ (security (addr *) (mode drop))
+
+ ?Varp security
+ Use esp too - none, auth, conf,
+ Varp sends broadcasts (requests) and unicasts (replies).
+ Uses UDP. Could send over ESP if needed.
+ For bcast don't know where it goes, so security has to be by vnet.
+ For ucast know where it goes, so could do by vnet and addr.
+
+ Similar issue for vnets: know where unicast goes but don't know where
+ bcast goes.
+
+ Simplify: 2 levels
+ local ucast
+ nonlocal ucast, mcast
+
+ (security (local none) (nonlocal conf))
+ (security (local auth) (nonlocal conf))
+
+ VARP security matches vnet security.
+
+ */
+
+/** @file
+ *
+ * Kernel interface to files in /proc.
+ */
+
+#define PROC_ROOT "/proc/"
+#define PROC_ROOT_LEN 6
+#define MODULE_ROOT PROC_ROOT "vnet"
+
+enum {
+ VNET_POLICY = 1,
+};
+
+typedef struct proc_dir_entry ProcEntry;
+typedef struct inode Inode;
+typedef struct file File;
+
+static int proc_open_fn(struct inode *inode, File *file);
+static ssize_t proc_read_fn(File *file, char *buffer, size_t count, loff_t *offset);
+static ssize_t proc_write_fn(File *file, const char *buffer, size_t count, loff_t *offset) ;
+//static int proc_flush_fn(File *file);
+static loff_t proc_lseek_fn(File * file, loff_t offset, int orig);
+static int proc_ioctl_fn(struct inode *inode, File *file, unsigned opcode, unsigned long arg);
+static int proc_release_fn(struct inode *inode, File *file);
+
+static int eval(Sxpr exp);
+
+static int ProcEntry_has_name(ProcEntry *entry, const char *name, int namelen){
+ dprintf("> name=%.*s entry=%.*s\n", namelen, name, entry->namelen, entry->name);
+ if(!entry || !entry->low_ino) return FALSE;
+ if(entry->namelen != namelen) return FALSE;
+ return memcmp(name, entry->name, namelen) == 0;
+}
+
+// Set f->f_error on error?
+// Does interface stop r/w on first error?
+// Is release called after an error?
+//
+
+static struct file_operations proc_file_ops = {
+ //owner: THIS_MODULE,
+ open: proc_open_fn,
+ read: proc_read_fn,
+ write: proc_write_fn,
+ //flush: proc_flush_fn,
+ llseek: proc_lseek_fn,
+ ioctl: proc_ioctl_fn,
+ release: proc_release_fn,
+};
+
+static int proc_get_parser(File *file, Parser **val){
+ int err = 0;
+ Parser *parser = NULL;
+ parser = file->private_data;
+ if(!parser){
+ parser = Parser_new();
+ if(!parser){
+ err = -ENOMEM;
+ goto exit;
+ }
+ file->private_data = parser;
+ }
+ exit:
+ *val = parser;
+ return err;
+}
+
+static int proc_open_fn(Inode *inode, File *file){
+ // User open.
+ // Return errcode or 0 on success.
+ // Can stuff data in file->private_data (void*).
+ // Get entry from
+ //ProcEntry *entry = (ProcEntry *)inode->u.generic_ip;
+ //file->private_data = NULL;
+ // Check for user privilege - deny otherwise.
+ // -EACCESS
+ int err = 0;
+ dprintf(">\n");
+ file->private_data = NULL;
+ return err;
+}
+
+static ssize_t proc_read_fn(File *file, char *buffer,
+ size_t count, loff_t *offset){
+ // User read.
+ // Copy data to user buffer, increment offset by count, return count.
+ dprintf(">\n");
+ count = 0;
+ //if(copy_to_user(buffer, data, count)){
+ // return -EFAULT;
+ //}
+ //*offset += count;
+ return count;
+}
+
+static ssize_t proc_write_fn(File *file, const char *buffer,
+ size_t count, loff_t *offset) {
+ // User write.
+ // Copy data into kernel space from buffer.
+ // Increment offset by count, return count (or code).
+ int err = 0;
+ char *data = NULL;
+ Parser *parser = NULL;
+
+ //dprintf("> count=%d\n", count);
+ err = proc_get_parser(file, &parser);
+ if(err) goto exit;
+ data = allocate(count);
+ if(!data){
+ err = -ENOMEM;
+ goto exit;
+ }
+ err = copy_from_user(data, buffer, count);
+ if(err) goto exit;
+ *offset += count;
+ err = Parser_input(parser, data, count);
+ exit:
+ deallocate(data);
+ err = (err < 0 ? err : count);
+ //dprintf("< err = %d\n", err);
+ return err;
+}
+
+#if 0
+static int proc_flush_fn(File *file){
+ // User flush.
+ int writing = (file->f_flags & O_ACCMODE) == O_WRONLY;
+ int f_count = atomic_read(&file->f_count);
+ if (writing && f_count == 1) {
+ ProcEntry *pentry = (ProcEntry *)file->f_dentry->d_inode->u.generic_ip;
+ // ...
+ }
+ return retval;
+}
+#endif
+
+#ifndef SEEK_SET
+enum {
+ /** Offset from start. */
+ SEEK_SET = 0,
+ /** Offset from current position. */
+ SEEK_CUR = 1,
+ /** Offset from size of file. */
+ SEEK_END = 2
+};
+#endif /* !SEEK_SET */
+
+static loff_t proc_lseek_fn(File * file, loff_t offset, int from){
+ // User lseek.
+ dprintf(">\n");
+ switch(from){
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += file->f_pos;
+ break;
+ case SEEK_END:
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+ if(offset < 0) return -EINVAL;
+ file->f_pos = offset;
+ return offset;
+}
+
+static int proc_ioctl_fn(Inode *inode, File *file,
+ unsigned opcode, unsigned long arg){
+ // User ioctl.
+ dprintf(">\n");
+ return 0;
+}
+
+static int proc_release_fn(Inode *inode, File *file){
+ // User close.
+ // Cleanup file->private_data, return errcode.
+ int err = 0;
+ Parser *parser = NULL;
+ Sxpr obj, l;
+
+ dprintf(">\n");
+ err = proc_get_parser(file, &parser);
+ if(err) goto exit;
+ err = Parser_input(parser, NULL, 0);
+ if(err) goto exit;
+ obj = parser->val;
+ objprint(iostdout, obj, 0); IOStream_print(iostdout, "\n");
+ for(l = obj; CONSP(l); l = CDR(l)){
+ err = eval(CAR(l));
+ if(err) break;
+ }
+ exit:
+ Parser_free(parser);
+ file->private_data = NULL;
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+static ProcEntry *proc_fs_root = &proc_root;
+
+static int proc_path_init(const char *path, const char **rest){
+ int err = 0;
+
+ if(!path){
+ err = -EINVAL;
+ goto exit;
+ }
+ if(*path == '/'){
+ if(strncmp(PROC_ROOT, path, PROC_ROOT_LEN)){
+ err = -EINVAL;
+ } else {
+ path += PROC_ROOT_LEN;
+ }
+ }
+ exit:
+ *rest = path;
+ return err;
+}
+
+
+/** Parse a path relative to `dir'. If dir is null or the proc root
+ * the path is relative to "/proc/", and the leading "/proc/" may be
+ * supplied.
+ *
+ */
+static ProcEntry * ProcFS_lookup(const char *path, ProcEntry *dir){
+ const char *pathptr = path, *next = NULL;
+ ProcEntry *entry, *result = NULL;
+ int pathlen;
+
+ if(dir && (dir != proc_fs_root)){
+ entry = dir;
+ } else {
+ if(proc_path_init(path, &pathptr)) goto exit;
+ entry = proc_fs_root;
+ }
+ if(!pathptr || !*pathptr) goto exit;
+ while(1){
+ next = strchr(pathptr, '/');
+ pathlen = (next ? next - pathptr : strlen(pathptr));
+ for(entry = entry->subdir; entry ; entry = entry->next) {
+ if(ProcEntry_has_name(entry, pathptr, pathlen)) break;
+ }
+ if (!entry) break;
+ if(!next){
+ result = entry;
+ break;
+ }
+ pathptr = next + 1;
+ }
+ exit:
+ return result;
+}
+
+static ProcEntry *ProcFS_register(const char *name, ProcEntry *dir, int val){
+ mode_t mode = 0;
+ ProcEntry *entry;
+
+ entry = create_proc_entry(name, mode, dir);
+ if(entry){
+ entry->proc_fops = &proc_file_ops;
+ entry->data = (void*)val; // Whatever data we need.
+ }
+ return entry;
+}
+
+static ProcEntry *ProcFS_mkdir(const char *name, ProcEntry *parent){
+ ProcEntry *entry = NULL;
+ entry = ProcFS_lookup(name, parent);
+ if(!entry){
+ const char *path;
+ if(proc_path_init(name, &path)) goto exit;
+ entry = proc_mkdir(path, parent);
+ }
+ exit:
+ return entry;
+}
+
+static void ProcFS_remove(const char *name, ProcEntry *parent){
+ remove_proc_entry(name, parent);
+}
+
+static void ProcFS_rmrec_entry(ProcEntry *entry){
+ if(entry){
+ // Don't want to remove /proc itself!
+ if(entry->parent == entry) return;
+ while(entry->subdir){
+ ProcFS_rmrec_entry(entry->subdir);
+ }
+ dprintf("> remove %s\n", entry->name);
+ ProcFS_remove(entry->name, entry->parent);
+ }
+}
+
+static void ProcFS_rmrec(const char *name, ProcEntry *parent){
+ ProcEntry *entry;
+
+ dprintf("> name=%s\n", name);
+ entry = ProcFS_lookup(name, parent);
+ if(entry){
+ ProcFS_rmrec_entry(entry);
+ }
+ dprintf("<\n");
+}
+
+static int stringof(Sxpr exp, char **s){
+ int err = 0;
+ if(ATOMP(exp)){
+ *s = atom_name(exp);
+ } else if(STRINGP(exp)){
+ *s = string_string(exp);
+ } else {
+ err = -EINVAL;
+ *s = NULL;
+ }
+ return err;
+}
+
+static int child_string(Sxpr exp, Sxpr key, char **s){
+ int err = 0;
+ Sxpr val = sxpr_child_value(exp, key, ONONE);
+ err = stringof(val, s);
+ return err;
+}
+
+static int intof(Sxpr exp, int *v){
+ int err = 0;
+ char *s;
+ unsigned long l;
+ if(INTP(exp)){
+ *v = OBJ_INT(exp);
+ } else {
+ err = stringof(exp, &s);
+ if(err) goto exit;
+ err = convert_atoul(s, &l);
+ *v = (int)l;
+ }
+ exit:
+ return err;
+}
+
+static int child_int(Sxpr exp, Sxpr key, int *v){
+ int err = 0;
+ Sxpr val = sxpr_child_value(exp, key, ONONE);
+ err = intof(val, v);
+ return err;
+}
+
+static int macof(Sxpr exp, unsigned char *v){
+ int err = 0;
+ char *s;
+ err = stringof(exp, &s);
+ if(err) goto exit;
+ err = mac_aton(s, v);
+ exit:
+ return err;
+}
+
+static int child_mac(Sxpr exp, Sxpr key, unsigned char *v){
+ int err = 0;
+ Sxpr val = sxpr_child_value(exp, key, ONONE);
+ err = macof(val, v);
+ return err;
+}
+
+static int addrof(Sxpr exp, uint32_t *v){
+ int err = 0;
+ char *s;
+ unsigned long w;
+ err = stringof(exp, &s);
+ if(err) goto exit;
+ err = get_inet_addr(s, &w);
+ if(err) goto exit;
+ *v = (uint32_t)w;
+ exit:
+ return err;
+}
+
+static int child_addr(Sxpr exp, Sxpr key, uint32_t *v){
+ int err = 0;
+ Sxpr val = sxpr_child_value(exp, key, ONONE);
+ err = addrof(val, v);
+ return err;
+}
+
+/** Create a vnet.
+ * It is an error if a vnet with the same id exists.
+ *
+ * @param vnet vnet id
+ * @param security security level
+ * @return 0 on success, error code otherwise
+ */
+static int ctrl_vnet_add(int vnet, int security){
+ int err = 0;
+ Vnet *vnetinfo = NULL;
+ if(Vnet_lookup(vnet, &vnetinfo) == 0){
+ err = -EEXIST;
+ goto exit;
+ }
+ err = Vnet_alloc(&vnetinfo);
+ if(err) goto exit;
+ vnetinfo->vnet = vnet;
+ vnetinfo->security = security;
+ err = Vnet_create(vnetinfo);
+ exit:
+ if(vnetinfo) Vnet_decref(vnetinfo);
+ return err;
+}
+
+/** Delete a vnet.
+ *
+ * @param vnet vnet id
+ * @return 0 on success, error code otherwise
+ */
+static int ctrl_vnet_del(int vnet){
+ int err = -ENOSYS;
+ // Can't delete if there are any vifs on the vnet.
+ //Vnet_del(vnet);
+ return err;
+}
+
+/** Create an entry for a vif with the given vnet and vmac.
+ *
+ * @param vnet vnet id
+ * @param vmac mac address
+ * @return 0 on success, error code otherwise
+ */
+static int ctrl_vif_add(int vnet, Vmac *vmac){
+ int err = 0;
+ Vnet *vnetinfo = NULL;
+ Vif *vif = NULL;
+
+ dprintf(">\n");
+ err = Vnet_lookup(vnet, &vnetinfo);
+ if(err) goto exit;
+ err = vif_add(vnet, vmac, &vif);
+ exit:
+ if(vnetinfo) Vnet_decref(vnetinfo);
+ if(vif) vif_decref(vif);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Add net device 'vifname' to the bridge for 'vnet' and
+ * create an entry for a vif with the given vnet and vmac.
+ * This is used when device 'vifname' is a virtual device
+ * connected to a vif in a vm.
+ *
+ * @param vifname name of device to bridge
+ * @param vnet vnet id
+ * @param vmac mac address
+ * @return 0 on success, error code otherwise
+ */
+static int ctrl_vif_conn(char *vifname, int vnet, Vmac *vmac){
+ int err = 0;
+ Vnet *vnetinfo = NULL;
+ struct net_device *vifdev = NULL;
+ Vif *vif = NULL;
+
+ dprintf("> %s\n", vifname);
+ err = Vnet_lookup(vnet, &vnetinfo);
+ if(err) goto exit;
+ err = vif_add(vnet, vmac, &vif);
+ if(err) goto exit;
+ err = vnet_get_device(vifname, &vifdev);
+ if(err) goto exit;
+ vif->dev = vifdev;
+ err = vnet_add_if(vnetinfo, vifdev);
+ exit:
+ if(vnetinfo) Vnet_decref(vnetinfo);
+ if(vif) vif_decref(vif);
+ if(vifdev) dev_put(vifdev);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Delete a vif.
+ *
+ * @param vnet vnet id
+ * @param vmac mac address
+ * @return 0 on success, error code otherwise
+ */
+static int ctrl_vif_del(int vnet, Vmac *vmac){
+ int err = 0;
+ Vnet *vnetinfo = NULL;
+ Vif *vif = NULL;
+
+ dprintf(">\n");
+ err = Vnet_lookup(vnet, &vnetinfo);
+ if(err) goto exit;
+ err = vif_lookup(vnet, vmac, &vif);
+ if(err) goto exit;
+ if(vif->dev){
+ vnet_del_if(vnetinfo, vif->dev);
+ vif->dev = NULL;
+ }
+ vif_remove(vnet, vmac);
+ exit:
+ if(vnetinfo) Vnet_decref(vnetinfo);
+ if(vif) vif_decref(vif);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** (varp.print)
+ */
+static int eval_varp_print(Sxpr exp){
+ int err = 0;
+ varp_print();
+ return err;
+}
+
+/** (varp.mcaddr (addr <addr>))
+ */
+static int eval_varp_mcaddr(Sxpr exp){
+ int err =0;
+ Sxpr oaddr = intern("addr");
+ uint32_t addr;
+
+ err = child_addr(exp, oaddr, &addr);
+ if(err < 0) goto exit;
+ varp_set_mcast_addr(addr);
+ exit:
+ return err;
+}
+
+/** (vnet.add (id <id>) [(security { none | auth | conf } )] )
+ */
+static int eval_vnet_add(Sxpr exp){
+ int err = 0;
+ Sxpr oid = intern("id");
+ Sxpr osecurity = intern("security");
+ Sxpr csecurity;
+ int id;
+ char *security;
+ int sec;
+ err = child_int(exp, oid, &id);
+ if(err) goto exit;
+ if(id < VNET_VIF){
+ err = -EINVAL;
+ goto exit;
+ }
+ csecurity = sxpr_child_value(exp, osecurity, intern("none"));
+ err = stringof(csecurity, &security);
+ if(err) goto exit;
+ if(strcmp(security, "none")==0){
+ sec = 0;
+ } else if(strcmp(security, "auth")==0){
+ sec = SA_AUTH;
+ } else if(strcmp(security, "conf")==0){
+ sec = SA_CONF;
+ } else {
+ err = -EINVAL;
+ goto exit;
+ }
+ dprintf("> vnet id=%d\n", id);
+ err = ctrl_vnet_add(id, sec);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Delete a vnet.
+ *
+ * (vnet.del (id <id>))
+ *
+ * @param vnet vnet id
+ * @return 0 on success, error code otherwise
+ */
+static int eval_vnet_del(Sxpr exp){
+ int err = 0;
+ Sxpr oid = intern("id");
+ int id;
+
+ err = child_int(exp, oid, &id);
+ if(err) goto exit;
+ err = ctrl_vnet_del(id);
+ exit:
+ return err;
+}
+
+/** (vif.add (vnet <vnet>) (vmac <macaddr>))
+ */
+static int eval_vif_add(Sxpr exp){
+ int err = 0;
+ Sxpr ovnet = intern("vnet");
+ Sxpr ovmac = intern("vmac");
+ int vnet;
+ Vmac vmac = {};
+
+ err = child_int(exp, ovnet, &vnet);
+ if(err) goto exit;
+ err = child_mac(exp, ovmac, vmac.mac);
+ if(err) goto exit;
+ err = ctrl_vif_add(vnet, &vmac);
+ exit:
+ return err;
+}
+
+/** (vif.conn (vif <name>) (vnet <id>) (vmac <mac>))
+ */
+static int eval_vif_conn(Sxpr exp){
+ int err = 0;
+ Sxpr ovif = intern("vif");
+ Sxpr ovnet = intern("vnet");
+ Sxpr ovmac = intern("vmac");
+ char *vif = NULL;
+ int vnet = 0;
+ Vmac vmac = {};
+
+ err = child_string(exp, ovif, &vif);
+ if(err) goto exit;
+ err = child_int(exp, ovnet, &vnet);
+ if(err) goto exit;
+ err = child_mac(exp, ovmac, vmac.mac);
+ dprintf("> connect vif=%s vnet=%d\n", vif, vnet);
+ err = ctrl_vif_conn(vif, vnet, &vmac);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** (vif.del (vnet <vnet>) (vmac <macaddr>))
+ */
+static int eval_vif_del(Sxpr exp){
+ int err = 0;
+ Sxpr ovnet = intern("vnet");
+ Sxpr ovmac = intern("vmac");
+ int vnet;
+ Vmac vmac = {};
+
+ err = child_int(exp, ovnet, &vnet);
+ if(err) goto exit;
+ err = child_mac(exp, ovmac, vmac.mac);
+ if(err) goto exit;
+ err = ctrl_vif_del(vnet, &vmac);
+ exit:
+ return err;
+}
+
+typedef struct SxprEval {
+ Sxpr elt;
+ int (*fn)(Sxpr);
+} SxprEval;
+
+static int eval(Sxpr exp){
+ int err = 0;
+ SxprEval defs[] = {
+ { intern("varp.print"), eval_varp_print },
+ { intern("varp.mcaddr"), eval_varp_mcaddr },
+ { intern("vif.add"), eval_vif_add },
+ { intern("vif.conn"), eval_vif_conn },
+ { intern("vif.del"), eval_vif_del },
+ { intern("vnet.add"), eval_vnet_add },
+ { intern("vnet.del"), eval_vnet_del },
+ { ONONE, NULL } };
+ SxprEval *def;
+
+ dprintf(">\n");
+ err = -EINVAL;
+ for(def = defs; !NONEP(def->elt); def++){
+ if(sxpr_elementp(exp, def->elt)){
+ err = def->fn(exp);
+ break;
+ }
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+void __init ProcFS_init(void){
+ ProcEntry *root_entry;
+ ProcEntry *policy_entry;
+
+ dprintf(">\n");
+ root_entry = ProcFS_mkdir(MODULE_ROOT, NULL);
+ if(!root_entry) goto exit;
+ policy_entry = ProcFS_register("policy", root_entry, VNET_POLICY);
+ exit:
+ dprintf("<\n");
+}
+
+void __exit ProcFS_exit(void){
+ dprintf(">\n");
+ ProcFS_rmrec(MODULE_ROOT, NULL);
+ dprintf("<\n");
+}
diff --git a/tools/vnet/vnet-module/vnet_ioctl.h b/tools/vnet/vnet-module/vnet_ioctl.h
new file mode 100644
index 0000000000..e57763284b
--- /dev/null
+++ b/tools/vnet/vnet-module/vnet_ioctl.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free software Foundation, Inc.,
+ * 59 Temple Place, suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#ifndef _VNET_VNET_IOCTL_H_
+#define _VNET_VNET_IOCTL_H_
+
+extern void ProcFS_init(void);
+extern void ProcFS_exit(void);
+
+#endif /* ! _VNET_VNET_IOCTL_H_ */
diff --git a/tools/vnet/vnetd/Makefile b/tools/vnet/vnetd/Makefile
new file mode 100644
index 0000000000..3783fa3833
--- /dev/null
+++ b/tools/vnet/vnetd/Makefile
@@ -0,0 +1,102 @@
+# -*- mode: Makefile; -*-
+#----------------------------------------------------------------------------
+# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>.
+#
+# This library is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as
+# published by the Free Software Foundation; either version 2.1 of the
+# License, or (at your option) any later version. This library is
+# distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#----------------------------------------------------------------------------
+
+all: vnetd
+
+#----------------------------------------------------------------------------
+
+XEN_ROOT = ../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+VNETD_INSTALL_DIR = /usr/sbin
+
+LIB_DIR = $(XEN_LIBXUTIL)
+VNET_DIR = ../vnet-module
+
+INCLUDES += -I$(LIB_DIR)
+INCLUDES += -I$(VNET_DIR)
+
+#----------------------------------------------------------------------------
+# GC.
+GC_DIR:=../gc/install
+GC_INCLUDE:= $(GC_DIR)/include
+GC_LIB_DIR:=$(GC_DIR)/lib
+
+INCLUDES += -I$(GC_INCLUDE)
+#LIBS += -L$(GC_LIB_DIR)
+CPPFLAGS += -D USE_GC
+
+#----------------------------------------------------------------------------
+CFLAGS += -Wall
+CFLAGS += $(INCLUDES) $(LIBS)
+
+LDFLAGS += $(LIBS)
+
+# Dependencies. Gcc generates them for us.
+CFLAGS += -Wp,-MD,.$(@F).d
+PROG_DEP = .*.d
+
+vpath %.c $(LIB_DIR)
+
+IPATHS:=$(INCLUDES:-I=)
+vpath %.h $(IPATHS)
+
+#----------------------------------------------------------------------------
+VNETD_SRC:=
+VNETD_SRC+= connection.c
+VNETD_SRC+= marshal.c
+VNETD_SRC+= select.c
+VNETD_SRC+= timer.c
+VNETD_SRC+= vcache.c
+VNETD_SRC+= vnetd.c
+
+LIB_SRC:=
+LIB_SRC+= allocate.c
+LIB_SRC+= enum.c
+LIB_SRC+= file_stream.c
+LIB_SRC+= hash_table.c
+LIB_SRC+= iostream.c
+LIB_SRC+= lexis.c
+LIB_SRC+= socket_stream.c
+LIB_SRC+= string_stream.c
+LIB_SRC+= sxpr.c
+LIB_SRC+= sys_net.c
+LIB_SRC+= sys_string.c
+LIB_SRC+= util.c
+
+VNETD_SRC+=$(LIB_SRC)
+
+VNETD_OBJ := $(VNETD_SRC:.c=.o)
+
+#VNETD_LIBS:= $(GC_LIB_DIR)/libgc.so.1.0.2
+#VNETD_LIBS:= -lgc
+VNETD_LIBS:= $(GC_LIB_DIR)/libgc.a
+
+vnetd: $(VNETD_OBJ)
+ $(CC) $(CFLAGS) -o $@ $^ $(VNETD_LIBS) -ldl -lpthread
+
+install: vnetd
+ mkdir -p $(DESTDIR)$(VNETD_INSTALL_DIR)
+ install -m 0755 vnetd $(DESTDIR)$(VNETD_INSTALL_DIR)
+
+clean:
+ -rm -f *.a *.o *~
+ -rm -f vnetd
+ -rm -f $(PROG_DEP)
+
+-include $(PROG_DEP)
diff --git a/tools/vnet/vnetd/connection.c b/tools/vnet/vnetd/connection.c
new file mode 100644
index 0000000000..6571f70762
--- /dev/null
+++ b/tools/vnet/vnetd/connection.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2003 - 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "allocate.h"
+#include "connection.h"
+#include "file_stream.h"
+#include "socket_stream.h"
+
+#define DEBUG
+#undef DEBUG
+#define MODULE_NAME "conn"
+#include "debug.h"
+
+/** Initialize a file stream from a file desciptor.
+ *
+ * @param fd file descriptor
+ * @param mode file mode
+ * @param buffered make the stream buffered if 1, unbuffered if 0
+ * @param io return parameter for the stream
+ * @return 0 on success, error code otherwise
+ */
+static int stream_init(int fd, const char *mode, int buffered, IOStream **io){
+ int err = 0;
+ *io = file_stream_fdopen(fd, mode);
+ if(!*io){
+ err = -errno;
+ perror("fdopen");
+ goto exit;
+ }
+ if(!buffered){
+ // Make unbuffered.
+ err = file_stream_setvbuf(*io, NULL, _IONBF, 0);
+ if(err){
+ err = -errno;
+ perror("setvbuf");
+ goto exit;
+ }
+ }
+ exit:
+ if(err && *io){
+ IOStream_close(*io);
+ *io = NULL;
+ }
+ return err;
+}
+
+ConnList * ConnList_add(Conn *conn, ConnList *l){
+ ConnList *v;
+ v = ALLOCATE(ConnList);
+ v->conn = conn;
+ v->next =l;
+ return v;
+}
+
+Conn *Conn_new(int (*fn)(Conn *), void *data){
+ Conn *conn;
+ conn = ALLOCATE(Conn);
+ conn->fn = fn;
+ conn->data = data;
+ return conn;
+}
+
+int Conn_handle(Conn *conn){
+ int err = 0;
+ dprintf(">\n");
+ if(conn->fn){
+ err = conn->fn(conn);
+ } else {
+ dprintf("> no handler\n");
+ err = -ENOSYS;
+ }
+ if(err < 0){
+ Conn_close(conn);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Initialize a connection.
+ *
+ * @param conn connection
+ * @param sock socket
+ * @param ipaddr ip address
+ * @return 0 on success, error code otherwise
+ */
+int Conn_init(Conn *conn, int sock, int type, struct sockaddr_in addr){
+ int err = 0;
+ conn->addr = addr;
+ conn->type = type;
+ conn->sock = sock;
+ if(type == SOCK_STREAM){
+ err = stream_init(sock, "r", 0, &conn->in);
+ if(err) goto exit;
+ err = stream_init(sock, "w", 0, &conn->out);
+ if(err) goto exit;
+ } else {
+ conn->in = socket_stream_new(sock);
+ conn->out = socket_stream_new(sock);
+ socket_stream_set_addr(conn->out, &addr);
+ }
+ exit:
+ if(err) eprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Open a connection.
+ *
+ * @param conn connection
+ * @param socktype socket type
+ * @param ipaddr ip address to connect to
+ * @param port port
+ * @return 0 on success, error code otherwise
+ */
+int Conn_connect(Conn *conn, int socktype, struct in_addr ipaddr, uint16_t port){
+ int err = 0;
+ int sock;
+ struct sockaddr_in addr_in;
+ struct sockaddr *addr = (struct sockaddr *)&addr_in;
+ socklen_t addr_n = sizeof(addr_in);
+ dprintf("> addr=%s:%d\n", inet_ntoa(ipaddr), ntohs(port));
+ sock = socket(AF_INET, socktype, 0);
+ if(sock < 0){
+ err = -errno;
+ goto exit;
+ }
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_addr = ipaddr;
+ addr_in.sin_port = port;
+ err = connect(sock, addr, addr_n);
+ if(err) goto exit;
+ err = Conn_init(conn, sock, socktype, addr_in);
+ exit:
+ if(err) eprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Close a connection.
+ *
+ * @param conn connection
+ */
+void Conn_close(Conn *conn){
+ if(!conn) return;
+ if(conn->in) IOStream_close(conn->in);
+ if(conn->out) IOStream_close(conn->out);
+ shutdown(conn->sock, 2);
+}
diff --git a/tools/vnet/vnetd/connection.h b/tools/vnet/vnetd/connection.h
new file mode 100644
index 0000000000..198f28ed90
--- /dev/null
+++ b/tools/vnet/vnetd/connection.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2003 - 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _VNET_CONNECTION_H_
+#define _VNET_CONNECTION_H_
+
+#include <netinet/in.h>
+
+#include "iostream.h"
+
+/** A connection.
+ * The underlying transport is a socket.
+ * Contains in and out streams using the socket.
+ */
+typedef struct Conn {
+ struct sockaddr_in addr;
+ int sock;
+ int type;
+ IOStream *in;
+ IOStream *out;
+ int (*fn)(struct Conn *);
+ void *data;
+} Conn;
+
+typedef struct ConnList {
+ Conn *conn;
+ struct ConnList *next;
+} ConnList;
+
+extern ConnList * ConnList_add(Conn *conn, ConnList *l);
+
+extern Conn * Conn_new(int (*fn)(struct Conn *), void *data);
+extern int Conn_init(Conn *conn, int sock, int type, struct sockaddr_in addr);
+extern int Conn_connect(Conn *conn, int type, struct in_addr ipaddr, uint16_t port);
+extern int Conn_handle(Conn *conn);
+extern void Conn_close(Conn *conn);
+
+#endif /* ! _VNET_CONNECTION_H_ */
diff --git a/tools/vnet/vnetd/marshal.c b/tools/vnet/vnetd/marshal.c
new file mode 100644
index 0000000000..694b6eaa05
--- /dev/null
+++ b/tools/vnet/vnetd/marshal.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <errno.h>
+#include "sys_net.h"
+#include "allocate.h"
+#include "marshal.h"
+
+#define MODULE_NAME "marshal"
+#define DEBUG
+#undef DEBUG
+#include "debug.h"
+
+#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0]))
+
+/* Messages are coded as msgid followed by message fields.
+ * Initial message on any channel is hello - so can check version
+ * compatibility.
+ *
+ * char* -> uint16_t:n <n bytes>
+ * ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes).
+ * optional fields go as '1' <val> or '0' (the 0/1 is 1 byte).
+ * lists go as ('1' <elt>)* '0'
+ */
+
+int marshal_flush(IOStream *io){
+ int err = 0;
+ err = IOStream_flush(io);
+ return err;
+}
+
+int marshal_bytes(IOStream *io, void *s, uint32_t s_n){
+ int err = 0;
+ int n;
+ n = IOStream_write(io, s, s_n);
+ if(n < 0){
+ err = n;
+ } else if (n < s_n){
+ dprintf("> Wanted %d, got %d\n", s_n, n);
+ err = -EIO;
+ }
+ return err;
+}
+
+int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){
+ int err = 0;
+ int n;
+ //dprintf("> s_n=%d\n", s_n);
+ n = IOStream_read(io, s, s_n);
+ //dprintf("> n=%d\n", n);
+ if(n < 0){
+ err = n;
+ } else if(n < s_n){
+ dprintf("> Wanted %d, got %d\n", s_n, n);
+ err = -EIO;
+ }
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int marshal_uint8(IOStream *io, uint8_t x){
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint8(IOStream *io, uint8_t *x){
+ return unmarshal_bytes(io, x, sizeof(*x));
+}
+
+int marshal_uint16(IOStream *io, uint16_t x){
+ x = htons(x);
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint16(IOStream *io, uint16_t *x){
+ int err = 0;
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ *x = ntohs(*x);
+ return err;
+}
+
+int marshal_int32(IOStream *io, int32_t x){
+ int err = 0;
+ //dprintf("> x=%d\n", x);
+ x = htonl(x);
+ err = marshal_bytes(io, &x, sizeof(x));
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unmarshal_int32(IOStream *io, int32_t *x){
+ int err = 0;
+ //dprintf(">\n");
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ *x = ntohl(*x);
+ //dprintf("< err=%d x=%d\n", err, *x);
+ return err;
+}
+
+int marshal_uint32(IOStream *io, uint32_t x){
+ int err = 0;
+ //dprintf("> x=%u\n", x);
+ x = htonl(x);
+ err = marshal_bytes(io, &x, sizeof(x));
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unmarshal_uint32(IOStream *io, uint32_t *x){
+ int err = 0;
+ //dprintf(">\n");
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ *x = ntohl(*x);
+ //dprintf("< err=%d x=%u\n", err, *x);
+ return err;
+}
+
+int marshal_uint64(IOStream *io, uint64_t x){
+ int err;
+ err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff));
+ if(err) goto exit;
+ err = marshal_uint32(io, (uint32_t) ( x & 0xffffffff));
+ exit:
+ return err;
+}
+
+int unmarshal_uint64(IOStream *io, uint64_t *x){
+ int err = 0;
+ uint32_t hi, lo;
+ err = unmarshal_uint32(io, &hi);
+ if(err) goto exit;
+ err = unmarshal_uint32(io, &lo);
+ *x = (((uint64_t) hi) << 32) | lo;
+ exit:
+ return err;
+}
+
+int marshal_net16(IOStream *io, net16_t x){
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net16(IOStream *io, net16_t *x){
+ int err = 0;
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ return err;
+}
+
+int marshal_net32(IOStream *io, net32_t x){
+ return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net32(IOStream *io, net32_t *x){
+ int err = 0;
+ err = unmarshal_bytes(io, x, sizeof(*x));
+ return err;
+}
+
+int marshal_string(IOStream *io, char *s, uint32_t s_n){
+ int err;
+ //dprintf("> s=%s\n", s);
+ err = marshal_uint32(io, s_n);
+ if(err) goto exit;
+ err = marshal_bytes(io, s, s_n);
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+int unmarshal_string(IOStream *io, char *s, uint32_t s_n){
+ int err = 0, val_n = 0;
+ //dprintf(">\n");
+ err = unmarshal_uint32(io, &val_n);
+ if(err) goto exit;
+ if(val_n >= s_n){
+ err = -EINVAL;
+ goto exit;
+ }
+ err = unmarshal_bytes(io, s, val_n);
+ if(err) goto exit;
+ s[val_n] = '\0';
+ exit:
+ //dprintf("< err=%d s=%s\n", err, s);
+ return err;
+}
+
+int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){
+ int err = 0, val_n = 0;
+ char *val = NULL;
+ //dprintf(">\n");
+ err = unmarshal_uint32(io, &val_n);
+ if(err) goto exit;
+ val = allocate(val_n + 1);
+ if(!val){
+ err = -ENOMEM;
+ goto exit;
+ }
+ err = unmarshal_bytes(io, val, val_n);
+ if(err) goto exit;
+ val[val_n] = '\0';
+ exit:
+ if(err){
+ if(val) deallocate(val);
+ val = NULL;
+ val_n = 0;
+ }
+ *s = val;
+ if(s_n) *s_n = val_n;
+ //dprintf("< err=%d s=%s\n", err, *s);
+ return err;
+}
diff --git a/tools/vnet/vnetd/marshal.h b/tools/vnet/vnetd/marshal.h
new file mode 100644
index 0000000000..d9cdfb0677
--- /dev/null
+++ b/tools/vnet/vnetd/marshal.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _XEN_LIB_MARSHAL_H_
+#define _XEN_LIB_MARSHAL_H_
+
+#include "iostream.h"
+
+/** A 16-bit uint in network order, e.g. a port number. */
+typedef uint16_t net16_t;
+
+/** A 32-bit uint in network order, e.g. an IP address. */
+typedef uint32_t net32_t;
+
+extern int marshal_flush(IOStream *io);
+
+extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n);
+extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n);
+
+extern int marshal_uint8(IOStream *io, uint8_t x);
+extern int unmarshal_uint8(IOStream *io, uint8_t *x);
+
+extern int marshal_uint16(IOStream *io, uint16_t x);
+extern int unmarshal_uint16(IOStream *io, uint16_t *x);
+
+extern int marshal_uint32(IOStream *io, uint32_t x);
+extern int unmarshal_uint32(IOStream *io, uint32_t *x);
+
+extern int marshal_int32(IOStream *io, int32_t x);
+extern int unmarshal_int32(IOStream *io, int32_t *x);
+
+extern int marshal_uint64(IOStream *io, uint64_t x);
+extern int unmarshal_uint64(IOStream *io, uint64_t *x);
+
+extern int marshal_net16(IOStream *io, net16_t x);
+extern int unmarshal_net16(IOStream *io, net16_t *x);
+
+extern int marshal_net32(IOStream *io, net32_t x);
+extern int unmarshal_net32(IOStream *io, net32_t *x);
+
+extern int marshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n);
+
+#endif /* ! _XEN_LIB_MARSHAL_H_ */
diff --git a/tools/vnet/vnetd/select.c b/tools/vnet/vnetd/select.c
new file mode 100644
index 0000000000..483f615822
--- /dev/null
+++ b/tools/vnet/vnetd/select.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2003 - 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "select.h"
+
+/** Zero all the file descriptor sets.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_zero(SelectSet *set){
+ set->n = 0;
+ FD_ZERO(&set->rd);
+ FD_ZERO(&set->wr);
+ FD_ZERO(&set->er);
+}
+
+/** Add a file descriptor to the write set.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_add_read(SelectSet *set, int fd){
+ FD_SET(fd, &set->rd);
+ if(fd > set->n) set->n = fd;
+}
+
+/** Add a file descriptor to the write set.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_add_write(SelectSet *set, int fd){
+ FD_SET(fd, &set->wr);
+ if(fd > set->n) set->n = fd;
+}
+
+/** Select on file descriptors.
+ *
+ * @param set select set
+ * @param timeout timeout (may be NULL for no timeout)
+ * @return 0 on success, -1 otherwise
+ */
+int SelectSet_select(SelectSet *set, struct timeval *timeout){
+ return select(set->n+1, &set->rd, &set->wr, &set->er, timeout);
+}
diff --git a/tools/vnet/vnetd/select.h b/tools/vnet/vnetd/select.h
new file mode 100644
index 0000000000..6547915567
--- /dev/null
+++ b/tools/vnet/vnetd/select.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2003 - 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _VFC_SELECT_H_
+#define _VFC_SELECT_H_
+
+/** Set of file descriptors for select.
+ */
+typedef struct SelectSet {
+ int n;
+ fd_set rd, wr, er;
+} SelectSet;
+
+extern void SelectSet_zero(SelectSet *set);
+extern void SelectSet_add_read(SelectSet *set, int fd);
+extern void SelectSet_add_write(SelectSet *set, int fd);
+extern int SelectSet_select(SelectSet *set, struct timeval *timeout);
+
+#endif /* ! _VFC_SELECT_H_ */
diff --git a/tools/vnet/vnetd/timer.c b/tools/vnet/vnetd/timer.c
new file mode 100644
index 0000000000..01147346ff
--- /dev/null
+++ b/tools/vnet/vnetd/timer.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "allocate.h"
+#include "timer.h"
+
+#define MODULE_NAME "TIMER"
+#undef DEBUG
+#define DEBUG 1
+#include "debug.h"
+
+static Timer *timers = NULL;
+
+/** Get the time now as a double (in seconds).
+ * Returns zero if could not get the time.
+ *
+ * @return time now
+ */
+double time_now(void){
+ struct timeval time;
+ if(gettimeofday(&time, NULL)) return 0.0;
+ return (double)time.tv_sec + (1.0e-6 * (double)time.tv_usec);
+}
+
+/** Set the process real-time timer to go off at a given expiry time.
+ * The timer will not be set to go off in less than 10 ms
+ * (even if the expiry time is sooner, or in the past).
+ *
+ * @param expiry time (in seconds)
+ * @return 0 on success, error code otherwise
+ */
+static int timer_set(double expiry){
+ struct itimerval val = {};
+ struct itimerval old = {};
+ double now, delay;
+ int err = 0;
+
+ if(expiry == 0.0){
+ val.it_value.tv_sec = 0;
+ val.it_value.tv_usec = 0;
+ } else {
+ now = time_now();
+ delay = expiry - now;
+ if(delay < 0.01) delay = 0.01;
+ val.it_value.tv_sec = (long)delay;
+ val.it_value.tv_usec = (long)((delay - (double)(long)delay) * 1.0e6);
+ }
+ err = setitimer(ITIMER_REAL, &val, &old);
+ return err;
+}
+
+static void Timer_free(Timer *z){
+#ifndef USE_GC
+ if(!z) return;
+ deallocate(z);
+#endif
+}
+
+/** Process any expired timers.
+ * Calls the functions of expired timers and removes them
+ * from the timer list.
+ * Reschedules the interval timer for the earliest expiring timer
+ * (if any).
+ *
+ * Should not be called from within the SIGALRM handler - set
+ * a flag there and call it later.
+ *
+ * @return 0 on success, error code otherwise.
+ */
+int process_timers(void){
+ double now = time_now();
+ Timer *curr, *next;
+ for(curr = timers; curr; curr = next){
+ next = curr->next;
+ if(curr->expiry > now) break;
+ if(curr->fn) curr->fn(curr);
+ Timer_free(curr);
+ }
+ timers = curr;
+ timer_set((curr ? curr->expiry : 0));
+ return 0;
+}
+
+Timer * Timer_set(double delay, TimerFn *fn, void *data){
+ // Get 'now'.
+ double now = time_now();
+ Timer *timer = NULL, *prev, *curr, *next;
+ timer = ALLOCATE(Timer);
+ if(!timer) goto exit;
+ // Add delay to now to get expiry time.
+ timer->expiry = now + delay;
+ timer->fn = fn;
+ timer->data = data;
+
+ // Insert timer in list ordered by (increasing) expiry time.
+ prev = NULL;
+ for(curr = timers; curr; prev = curr, curr = next){
+ next = curr->next;
+ if(timer->expiry < curr->expiry) break;
+ }
+ if(prev){
+ prev->next = timer;
+ } else {
+ timers = timer;
+ }
+ timer->next = curr;
+
+ // Set interval timer to go off for earliest expiry time.
+ timer_set(timer->expiry);
+ exit:
+ return timer;
+}
+
+int Timer_cancel(Timer *timer){
+ // Remove timer from list.
+ int err = -ENOENT;
+ Timer *prev, *curr, *next;
+ for(prev = NULL, curr = timers; curr; prev = curr, curr = next){
+ next = curr->next;
+ if(curr == timer){
+ err = 0;
+ if(prev){
+ prev->next = curr->next;
+ } else {
+ timers = curr->next;
+ }
+ curr->next = NULL;
+ Timer_free(curr);
+ break;
+ }
+ }
+ return err;
+}
+
diff --git a/tools/vnet/vnetd/timer.h b/tools/vnet/vnetd/timer.h
new file mode 100644
index 0000000000..8342efd8e5
--- /dev/null
+++ b/tools/vnet/vnetd/timer.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _VNET_TIMER_H_
+#define _VNET_TIMER_H_
+
+struct Timer;
+
+typedef void TimerFn(struct Timer *);
+
+typedef struct Timer {
+ TimerFn *fn;
+ void *data;
+ double expiry;
+ struct Timer *next;
+} Timer;
+
+extern void timer_alarm(void);
+extern double time_now(void);
+extern int process_timers(void);
+extern Timer * Timer_set(double delay, TimerFn *fn, void *data);
+extern int Timer_cancel(Timer *timer);
+
+#endif /* ! _VNET_TIMER_H_ */
diff --git a/tools/vnet/vnetd/vcache.c b/tools/vnet/vnetd/vcache.c
new file mode 100644
index 0000000000..1ea81ba292
--- /dev/null
+++ b/tools/vnet/vnetd/vcache.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "allocate.h"
+#include "hash_table.h"
+#include "sys_net.h"
+#include "sys_string.h"
+#include "connection.h"
+#include "marshal.h"
+#include "timer.h"
+
+#undef offsetof
+#include "vnetd.h"
+#include "vcache.h"
+
+#define MODULE_NAME "VARP"
+#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+static VarpCache *vcache = NULL;
+
+void IPMessageQueue_init(IPMessageQueue *queue, int maxlen){
+ queue->msg = NULL;
+ queue->len = 0;
+ queue->maxlen = maxlen;
+}
+
+void IPMessageQueue_clear(IPMessageQueue *queue){
+ queue->msg = NULL;
+ queue->len = 0;
+}
+
+void IPMessageQueue_truncate(IPMessageQueue *queue, int n){
+ IPMessage **p = &queue->msg;
+ int i;
+ for(i = 1; *p; p = &(*p)->next, i++){
+ if(i == n){
+ *p = NULL;
+ break;
+ }
+ }
+}
+
+void IPMessageQueue_add(IPMessageQueue *queue, IPMessage *msg){
+ msg->next = queue->msg;
+ queue->msg = msg;
+ queue->len++;
+ if(queue->len >= queue->maxlen){
+ IPMessageQueue_truncate(queue, queue->maxlen);
+ }
+}
+
+IPMessage * IPMessageQueue_pop(IPMessageQueue *queue){
+ IPMessage *msg = NULL;
+ if(queue->len > 0){
+ queue->len--;
+ msg = queue->msg;
+ queue->msg = msg->next;
+ msg->next = NULL;
+ }
+ return msg;
+}
+
+void VarpCache_sweep(VarpCache *z, int all);
+
+/** Send a varp protocol message.
+ *
+ * @param opcode varp opcode (host order)
+ * @param vnet vnet id (in network order)
+ * @param vmac vmac (in network order)
+ * @return 0 on success, error code otherwise
+ */
+int varp_send(Conn *conn, uint16_t opcode, uint32_t vnet, Vmac *vmac, uint32_t addr){
+ int err = 0;
+ int varp_n = sizeof(VarpHdr);
+ VarpHdr varph = {};
+
+ varph.vnetmsghdr.id = htons(VARP_ID);
+ varph.vnetmsghdr.opcode = htons(opcode);
+ varph.vnet = vnet;
+ varph.vmac = *vmac;
+ varph.addr = addr;
+
+ if(0){
+ struct sockaddr_in self;
+ socklen_t self_n;
+ getsockname(conn->sock, (struct sockaddr *)&self, &self_n);
+ dprintf("> sockname addr=%s port=%d\n",
+ inet_ntoa(self.sin_addr), ntohs(self.sin_port));
+ }
+ dprintf("> addr=%s opcode=%d\n",
+ inet_ntoa(conn->addr.sin_addr), opcode);
+ dprintf("> vnet=%d vmac=" MACFMT " addr=" IPFMT "\n",
+ ntohl(vnet), MAC6TUPLE(vmac->mac), NIPQUAD(addr));
+ err = marshal_bytes(conn->out, &varph, varp_n);
+ marshal_flush(conn->out);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/* Test some flags.
+ *
+ * @param z varp entry
+ * @param flags to test
+ * @return nonzero if flags set
+ */
+int VCEntry_get_flags(VCEntry *z, int flags){
+ return z->flags & flags;
+}
+
+/** Set some flags.
+ *
+ * @param z varp entry
+ * @param flags to set
+ * @param set set flags on if nonzero, off if zero
+ * @return new flags value
+ */
+int VCEntry_set_flags(VCEntry *z, int flags, int set){
+ if(set){
+ z->flags |= flags;
+ } else {
+ z->flags &= ~flags;
+ }
+ return z->flags;
+}
+
+/** Print a varp entry.
+ *
+ * @param ventry varp entry
+ */
+void VCEntry_print(VCEntry *ventry){
+ if(ventry){
+ char *c, *d;
+ switch(ventry->state){
+ case VCACHE_STATE_INCOMPLETE: c = "INC"; break;
+ case VCACHE_STATE_REACHABLE: c = "RCH"; break;
+ case VCACHE_STATE_FAILED: c = "FLD"; break;
+ default: c = "UNK"; break;
+ }
+ d = (VCEntry_get_flags(ventry, VCACHE_FLAG_PROBING) ? "P" : " ");
+
+ printf("VENTRY(%p %s %s vnet=%d vmac=" MACFMT " addr=" IPFMT " time=%g)\n",
+ ventry,
+ c, d,
+ ntohl(ventry->key.vnet),
+ MAC6TUPLE(ventry->key.vmac.mac),
+ NIPQUAD(ventry->addr),
+ ventry->timestamp);
+ } else {
+ printf("VENTRY: Null!\n");
+ }
+}
+
+int VCEntry_schedule(VCEntry *ventry);
+void VCEntry_solicit(VCEntry *ventry);
+
+/** Function called when a varp entry timer goes off.
+ * If the entry is still incomplete, carries on probing.
+ * Otherwise stops probing.
+ *
+ * @param arg ventry
+ */
+static void ventry_timer_fn(Timer *timer){
+ VCEntry *ventry = timer->data;
+ int probing = 0, scheduled = 0;
+
+ //dprintf(">\n"); VCEntry_print(ventry);
+ if(ventry->state == VCACHE_STATE_REACHABLE){
+ // Do nothing.
+ } else {
+ // Probe if haven't run out of tries, otherwise fail.
+ if(ventry->probes < VCACHE_PROBE_MAX){
+ //probing = 1;
+ ventry->probes++;
+ scheduled = VCEntry_schedule(ventry);
+ //VCEntry_solicit(ventry);
+ probing = scheduled;
+ } else {
+ ventry->state = VCACHE_STATE_FAILED;
+ IPMessageQueue_clear(&ventry->queue);
+ }
+ }
+ if(!probing){
+ VCEntry_set_flags(ventry,
+ (VCACHE_FLAG_PROBING
+ | VCACHE_FLAG_REMOTE_PROBE
+ | VCACHE_FLAG_LOCAL_PROBE),
+ 0);
+ }
+ VCEntry_set_flags(ventry, VCACHE_FLAG_PROBING, probing);
+ //dprintf("<\n");
+}
+
+/** Schedule the varp entry timer.
+ *
+ * @param ventry varp entry
+ */
+int VCEntry_schedule(VCEntry *ventry){
+ int scheduled = 0;
+ if(ventry->probes == 1){
+ scheduled = 1;
+ Timer_set(VCACHE_LOCAL_DELAY, ventry_timer_fn, ventry);
+ } else {
+ VCEntry_solicit(ventry);
+ }
+ return scheduled;
+}
+
+/** Create a varp entry. Initializes the internal state.
+ *
+ * @param vnet vnet id
+ * @param vmac virtual MAC address (copied)
+ * @return ventry or null
+ */
+VCEntry * VCEntry_new(uint32_t vnet, Vmac *vmac){
+ VCEntry *z = ALLOCATE(VCEntry);
+ z->state = VCACHE_STATE_INCOMPLETE;
+ z->timestamp = time_now();
+ z->key.vnet = vnet;
+ z->key.vmac = *vmac;
+ return z;
+}
+
+/** Hash function for keys in the varp cache.
+ * Hashes the vnet id and mac.
+ *
+ * @param k key (VCKey)
+ * @return hashcode
+ */
+Hashcode vcache_key_hash_fn(void *k){
+ VCKey *key = k;
+ Hashcode h;
+ h = hash_2ul(key->vnet,
+ (key->vmac.mac[0] << 24) |
+ (key->vmac.mac[1] << 16) |
+ (key->vmac.mac[2] << 8) |
+ (key->vmac.mac[3] ));
+ h = hash_hul(h,
+ (key->vmac.mac[4] << 8) |
+ (key->vmac.mac[5] ));
+ return h;
+}
+
+/** Test equality for keys in the varp cache.
+ * Compares vnet and mac.
+ *
+ * @param k1 key to compare (VCKey)
+ * @param k2 key to compare (VCKey)
+ * @return 1 if equal, 0 otherwise
+ */
+int vcache_key_equal_fn(void *k1, void *k2){
+ VCKey *key1 = k1;
+ VCKey *key2 = k2;
+ return (key1->vnet == key2->vnet)
+ && (memcmp(key1->vmac.mac, key2->vmac.mac, ETH_ALEN) == 0);
+}
+
+void VarpCache_schedule(VarpCache *z);
+
+/** Function called when the varp table timer goes off.
+ * Sweeps old varp cache entries and reschedules itself.
+ *
+ * @param arg varp table
+ */
+static void vcache_timer_fn(Timer *timer){
+ VarpCache *z = timer->data;
+ //dprintf("> z=%p\n", z);
+ if(z){
+ VarpCache_sweep(z, 0);
+ VarpCache_schedule(z);
+ }
+ //dprintf("<\n");
+}
+
+/** Schedule the varp table timer.
+ *
+ * @param z varp table
+ */
+void VarpCache_schedule(VarpCache *z){
+ Timer_set(VCACHE_ENTRY_TTL, vcache_timer_fn, z);
+}
+
+/** Print a varp table.
+ *
+ * @param z table
+ */
+void VarpCache_print(VarpCache *z){
+ HashTable_for_decl(entry);
+ VCEntry *ventry;
+
+ dprintf(">\n");
+ HashTable_for_each(entry, vcache->table){
+ ventry = entry->value;
+ VCEntry_print(ventry);
+ }
+ dprintf("<\n");
+}
+
+/** Print the varp cache.
+ */
+void vcache_print(void){
+ VarpCache_print(vcache);
+}
+
+/** Create a varp table.
+ *
+ * @return new table or null
+ */
+VarpCache * VarpCache_new(void){
+ VarpCache *z = NULL;
+
+ z = ALLOCATE(VarpCache);
+ z->table = HashTable_new(VCACHE_BUCKETS);
+ z->table->key_equal_fn = vcache_key_equal_fn;
+ z->table->key_hash_fn = vcache_key_hash_fn;
+ VarpCache_schedule(z);
+ return z;
+}
+
+/** Add a new entry to the varp table.
+ *
+ * @param z table
+ * @param vnet vnet id
+ * @param vmac virtual MAC address (copied)
+ * @return new entry or null
+ */
+VCEntry * VarpCache_add(VarpCache *z, uint32_t vnet, Vmac *vmac){
+ VCEntry *ventry;
+ HTEntry *entry;
+
+ ventry = VCEntry_new(vnet, vmac);
+ //dprintf("> "); VCEntry_print(ventry);
+ entry = HashTable_add(z->table, ventry, ventry);
+ return ventry;
+}
+
+/** Remove an entry from the varp table.
+ *
+ * @param z table
+ * @param ventry entry to remove
+ * @return removed count
+ */
+int VarpCache_remove(VarpCache *z, VCEntry *ventry){
+ return HashTable_remove(z->table, ventry);
+}
+
+/** Lookup an entry in the varp table.
+ *
+ * @param z table
+ * @param vnet vnet id
+ * @param vmac virtual MAC addres
+ * @return entry found or null
+ */
+VCEntry * VarpCache_lookup(VarpCache *z, uint32_t vnet, Vmac *vmac){
+ VCKey key = { .vnet = vnet, .vmac = *vmac };
+ VCEntry *ventry;
+ ventry = HashTable_get(z->table, &key);
+ return ventry;
+}
+
+void VCEntry_solicit(VCEntry *ventry){
+ dprintf(">\n");
+ if(VCEntry_get_flags(ventry, VCACHE_FLAG_LOCAL_PROBE)){
+ dprintf("> local probe\n");
+ varp_send(vnetd->bcast_conn, VARP_OP_REQUEST, ventry->key.vnet, &ventry->key.vmac, ventry->addr);
+ }
+ if(VCEntry_get_flags(ventry, VCACHE_FLAG_REMOTE_PROBE)){
+ ConnList *l;
+ dprintf("> remote probe\n");
+ for(l = vnetd->connections; l; l = l->next){
+ varp_send(l->conn, VARP_OP_REQUEST, ventry->key.vnet, &ventry->key.vmac, ventry->addr);
+ }
+
+ }
+ dprintf("<\n");
+}
+
+int VCEntry_resolve(VCEntry *ventry, IPMessage *msg, int flags){
+ int err = 0;
+
+ dprintf("> "); //VCEntry_print(ventry);
+ ventry->state = VCACHE_STATE_INCOMPLETE;
+ VCEntry_set_flags(ventry, flags, 1);
+ IPMessageQueue_add(&ventry->queue, msg);
+ if(!VCEntry_get_flags(ventry, VCACHE_FLAG_PROBING)){
+ VCEntry_set_flags(ventry, VCACHE_FLAG_PROBING, 1);
+ ventry->probes = 1;
+ VCEntry_schedule(ventry);
+ //VCEntry_solicit(ventry);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Update a ventry. Sets the address and state to those given
+ * and sets the timestamp to 'now'.
+ *
+ * @param ventry varp entry
+ * @param addr care-of address
+ * @param state state
+ * @return 0 on success, error code otherwise
+ */
+int VCEntry_update(VCEntry *ventry, IPMessage *msg, VarpHdr *varph, int state){
+ int err = 0;
+ double now = time_now();
+
+ if(VCEntry_get_flags(ventry, VCACHE_FLAG_PERMANENT)) goto exit;
+ ventry->addr = varph->addr;
+ ventry->timestamp = now;
+ ventry->state = state;
+ if(ventry->state == VCACHE_STATE_REACHABLE){
+ // Process the output queue.
+ IPMessage *msg;
+ while((msg = IPMessageQueue_pop(&ventry->queue))){
+ dprintf("> announce\n");
+ varp_send(msg->conn, VARP_OP_ANNOUNCE, ventry->key.vnet, &ventry->key.vmac, ventry->addr);
+ }
+ }
+ exit:
+ return err;
+}
+
+/** Update the ventry corresponding to the given varp header.
+ *
+ * @param z table
+ * @param varph varp header
+ * @param state state
+ * @return 0 on success, -ENOENT if no entry found
+ */
+int VarpCache_update(VarpCache *z, IPMessage *msg, VarpHdr *varph, int state){
+ int err = 0;
+ VCEntry *ventry;
+
+ dprintf(">\n");
+ ventry = VarpCache_lookup(z, varph->vnet, &varph->vmac);
+ if(ventry){
+ err = VCEntry_update(ventry, msg, varph, state);
+ } else {
+ err = -ENOENT;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+
+/** Put old varp entries into the incomplete state.
+ * Permanent entries are not changed.
+ * If 'all' is non-zero, all non-permanent entries
+ * are put into the incomplete state, regardless of age.
+ *
+ * @param z table
+ * @param all reset all entries if non-zero
+ */
+void VarpCache_sweep(VarpCache *z, int all){
+ HashTable_for_decl(entry);
+ VCEntry *ventry;
+ double now = time_now();
+ double old = now - VCACHE_ENTRY_TTL;
+
+ dprintf(">\n");
+ HashTable_for_each(entry, vcache->table){
+ ventry = entry->value;
+ if(!VCEntry_get_flags(ventry, VCACHE_FLAG_PERMANENT) &&
+ (all || (ventry->timestamp < old))){
+ ventry->state = VCACHE_STATE_INCOMPLETE;
+ }
+ }
+ dprintf("<\n");
+}
+
+/** Forward a varp message.
+ * If local forwards it to remote vnetds.
+ * If not local forwards it to local net.
+ *
+ * @param varph varp message to forward
+ * @param local whether it's local or not
+ */
+void vcache_forward_varp(VarpHdr *varph, int local){
+ uint16_t opcode = ntohs(varph->vnetmsghdr.opcode);
+ if(local){
+ ConnList *l;
+ for(l = vnetd->connections; l; l = l->next){
+ varp_send(l->conn, opcode, varph->vnet, &varph->vmac, varph->addr);
+ }
+ } else {
+ varp_send(vnetd->bcast_conn, opcode, varph->vnet, &varph->vmac, varph->addr);
+ }
+}
+
+/** Handle a varp request.
+ *
+ * @param msg incoming message
+ * @param varph varp message
+ * @return 0 if ok, -ENOENT if no matching vif, or error code
+ */
+#if 1
+int vcache_handle_request(IPMessage *msg, VarpHdr *varph, int local){
+ dprintf("> local=%d\n", local);
+ vcache_forward_varp(varph, local);
+ dprintf("<\n");
+ return 0;
+}
+
+#else
+int vcache_handle_request(IPMessage *msg, VarpHdr *varph, int local){
+ int err = -ENOENT;
+ uint32_t vnet;
+ Vmac *vmac;
+ VCEntry *ventry = NULL;
+ int reply = 0;
+
+ dprintf(">\n");
+ vnet = htonl(varph->vnet);
+ vmac = &varph->vmac;
+ ventry = VarpCache_lookup(vcache, vnet, vmac);
+ if(!ventry){
+ ventry = VarpCache_add(vcache, vnet, vmac);
+ }
+ if(local){
+ // Request coming from the local subnet (on our udp port).
+ if(ventry->state == VCACHE_STATE_REACHABLE){
+ if(local){
+ // Have an entry, and it's non-local - reply (locally).
+ // Potential out-of-date cache problem.
+ // Should query remotely instead of replying.
+ varp_send(conn, VARP_OP_ANNOUNCE, ventry);
+ }
+ } else {
+ // Incomplete entry. Resolve.
+ VCEntry_resolve(ventry, msg, VCACHE_FLAG_REMOTE_PROBE);
+ }
+ } else {
+ // Non-local request (on one of our tcp connetions).
+ if(ventry->state == VCACHE_STATE_REACHABLE){
+ if(local){
+ // Have an entry and it's local - reply (remotely).
+ // Potential out-of-date cache problem.
+ // Should query locally instead of replying.
+ varp_send(msg->conn, VARP_OP_ANNOUNCE, ventry);
+ } else {
+ // Have a non-local entry - do nothing and assume someone else
+ // will reply.
+ }
+ } else {
+ // Incomplete entry. Resolve.
+ VCEntry_resolve(ventry, msg, VCACHE_FLAG_LOCAL_PROBE);
+ }
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+#endif
+
+/** Handle a varp announce message.
+ * Update the matching ventry if we have one.
+ *
+ * @param msg incoming message
+ * @param varp message
+ * @return 0 if OK, -ENOENT if no matching entry
+ */
+int vcache_handle_announce(IPMessage *msg, VarpHdr *varph, int local){
+ int err = 0;
+
+ vcache_forward_varp(varph, local);
+ err = VarpCache_update(vcache, msg, varph, VCACHE_STATE_REACHABLE);
+ return err;
+}
+
+/** Handle an incoming varp message.
+ *
+ * @param msg incoming message
+ * @return 0 if OK, error code otherwise
+ */
+int vcache_handle_message(IPMessage *msg, int local){
+ int err = -EINVAL;
+ VnetMsg *vmsg = msg->data;
+ VarpHdr *varph = &vmsg->varp.varph;
+
+ dprintf(">\n");
+ if(1){
+ dprintf("> src=%s:%d\n", inet_ntoa(msg->saddr.sin_addr), ntohs(msg->saddr.sin_port));
+ dprintf("> dst=%s:%d\n", inet_ntoa(msg->daddr.sin_addr), ntohs(msg->daddr.sin_port));
+ dprintf("> opcode=%d vnet=%u vmac=" MACFMT "\n",
+ ntohs(varph->opcode), ntohl(varph->vnet), MAC6TUPLE(varph->vmac.mac));
+ }
+ switch(ntohs(varph->vnetmsghdr.opcode)){
+ case VARP_OP_REQUEST:
+ err = vcache_handle_request(msg, varph, local);
+ break;
+ case VARP_OP_ANNOUNCE:
+ err = vcache_handle_announce(msg, varph, local);
+ break;
+ default:
+ break;
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Initialize the varp cache.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int vcache_init(void){
+ int err = 0;
+
+ if(!vcache){
+ vcache = VarpCache_new();
+ }
+ return err;
+}
diff --git a/tools/vnet/vnetd/vcache.h b/tools/vnet/vnetd/vcache.h
new file mode 100644
index 0000000000..1b0f492f83
--- /dev/null
+++ b/tools/vnet/vnetd/vcache.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _VNET_VCACHE_H_
+#define _VNET_VCACHE_H_
+
+#include "hash_table.h"
+
+/** Time-to-live of varp cache entries (in seconds).*/
+#define VCACHE_ENTRY_TTL 30.0
+
+/** Maximum number of varp probes to make. */
+#define VCACHE_PROBE_MAX 5
+
+/** Interval between varp probes (in seconds). */
+#define VCACHE_PROBE_INTERVAL 3.0
+
+/** Delay before forwarding a local probe (in seconds). */
+#define VCACHE_LOCAL_DELAY 2.0
+
+/** Number of buckets in the varp cache (must be prime). */
+#define VCACHE_BUCKETS 3001
+
+enum {
+ VCACHE_STATE_INCOMPLETE = 1,
+ VCACHE_STATE_REACHABLE = 2,
+ VCACHE_STATE_FAILED = 3
+};
+
+enum {
+ VCACHE_FLAG_PROBING = 1,
+ VCACHE_FLAG_PERMANENT = 2,
+ VCACHE_FLAG_LOCAL_PROBE = 4,
+ VCACHE_FLAG_REMOTE_PROBE = 8,
+};
+
+
+#include <asm/byteorder.h>
+/*
+ * Display an IP address in readable format.
+ */
+
+#define NIPQUAD(addr) \
+ ((unsigned char *)&addr)[0], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[3]
+
+#if defined(__LITTLE_ENDIAN)
+#define HIPQUAD(addr) \
+ ((unsigned char *)&addr)[3], \
+ ((unsigned char *)&addr)[2], \
+ ((unsigned char *)&addr)[1], \
+ ((unsigned char *)&addr)[0]
+#elif defined(__BIG_ENDIAN)
+#define HIPQUAD NIPQUAD
+#else
+#error "Please fix asm/byteorder.h"
+#endif /* __LITTLE_ENDIAN */
+
+#define IPFMT "%u.%u.%u.%u"
+#define MACFMT "%02x:%02x:%02x:%02x:%02x:%02x"
+
+#define MAC6TUPLE(_mac) (_mac)[0], (_mac)[1], (_mac)[2], (_mac)[3], (_mac)[4], (_mac)[5]
+
+typedef struct IPMessage {
+ Conn *conn;
+ struct sockaddr_in saddr;
+ struct sockaddr_in daddr;
+ void *data;
+ struct IPMessage *next;
+} IPMessage;
+
+typedef struct IPMessageQueue {
+ IPMessage *msg;
+ int len;
+ int maxlen;
+} IPMessageQueue;
+
+/** Key for varp cache entries. */
+typedef struct VCKey {
+ /** Vnet id (network order). */
+ uint32_t vnet;
+ /** Virtual MAC address. */
+ Vmac vmac;
+} VCKey;
+
+typedef struct VCEntry {
+ /** Key for the entry. */
+ VCKey key;
+
+ /** Care-of address for the key. */
+ uint32_t addr;
+
+ /** Alias coa if we are a gateway. */
+ //uint32_t gateway;
+ /** Encapsulation to use (if a gateway). */
+ //uint32_t encaps;
+
+ /** Where this entry came from. */
+ uint32_t source;
+
+ /** Last-updated timestamp. */
+ double timestamp;
+
+ /** State. */
+ short state;
+
+ /** Flags. */
+ short flags;
+
+ /** Number of probes sent. */
+ int probes;
+
+ /** List of messages to reply to when completes. */
+ IPMessageQueue queue;
+
+} VCEntry;
+
+/** The varp cache. Varp cache entries indexed by VCKey. */
+typedef struct VarpCache {
+ HashTable *table;
+} VarpCache;
+
+int vcache_init(void);
+int vcache_handle_message(IPMessage *msg, int local);
+
+#endif /* ! _VNET_VCACHE_H_ */
diff --git a/tools/vnet/vnetd/vnetd.c b/tools/vnet/vnetd/vnetd.c
new file mode 100644
index 0000000000..5a37e160ef
--- /dev/null
+++ b/tools/vnet/vnetd/vnetd.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/** @file
+ *
+ * Vnetd tcp messages:
+ *
+ * - varp request: request care-of-addr for a vif.
+ * If know answer, reply. If not broadcast locally.
+ *
+ * - varp announce: reply to a varp request.
+ * If a (local) request is pending, remember and broadcast locally.
+ *
+ * - vnet subscribe: indicate there are local vifs in a vnet (use varp announce?).
+ *
+ * - vnet forward: tunneled broadcast packet to rebroadcast.
+ * Broadcast locally (if there are vifs in the vnet).
+ *
+ *
+ * Vnetd udp messages (varp):
+ *
+ * - local varp request:
+ * If know and vif is non-local, reply.
+ * If know and vif is local, do nothing (but announce will reset).
+ * If have entry saying is local and no-one answers - remove (? or rely on entry timeout).
+ * If don't know and there is no (quick) local reply, forward to peers.
+ *
+ * - remote varp request:
+ * If know, reply.
+ * If don't know, query locally (and queue request).
+ *
+ * - varp announce: remember and adjust vnet subscriptions.
+ * Forward to peers if a request is pending.
+ *
+ * Vnetd broadcast messages (tunneling):
+ *
+ * - etherip: forward to peers (on the right vnets)
+ *
+ * - esp: forward to peers (on the right vnets)
+ *
+ *
+ * For etherip can tell the vnet from the header (in clear).
+ * But for esp can't. So should use mcast to define? Or always some clear header?
+ *
+ * Make ssl on tcp connections optional.
+ *
+ * So far have been assuming esp for security.
+ * But could use vnetd to forward and use ssl on the connection.
+ * But has usual probs with efficiency.
+ * However, should 'just work' if the coa for the vif has been set
+ * to the vnetd. How? Vnetd configured to act as gateway for
+ * some peers? Then would rewrite varp announce to itself and forward
+ * traffic to peer.
+ *
+ * Simplify - make each vnetd have one peer?
+ * If need to link more subnets, add vnetds?
+ *
+ * Need requests table for each tcp conn (incoming).
+ * - entries we want to resolve (and fwd the answer).
+ *
+ * Need requests table for the udp socket.
+ * - entries we want to resolve (and return the answer).
+ *
+ * Need table of entries we know.
+ * - from caching local announce
+ * - from caching announce reply to forwarded request
+ *
+ * Problem with replying to requests from the cache - if the cache
+ * is out of date we reply with incorrect data. So if a VM migrates
+ * we will advertise the old location until it times out.
+ *
+ * So should probably not reply out of the cache at all - but always
+ * query for the answer. Could query direct to old location if
+ * entry is valid the first time, and broadcast if no reply in timeout.
+ * Causes delay if migrated - may as well broadcast.
+ *
+ * Need to watch out for query loops. If have 3 vnetds A,B,C and
+ * A gets a query, forwards to B and C. B forwards to C, which
+ * forwards to A, and on forever. So if have an entry that has been
+ * probed, do not forward it when get another query for it.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+
+//#include </usr/include/linux/ip.h> // For struct iphdr;
+#include <linux/ip.h> // For struct iphdr;
+
+#include <linux/if_ether.h>
+#include "if_etherip.h"
+#include "if_varp.h"
+
+#include "allocate.h"
+
+#include "vnetd.h"
+#include "file_stream.h"
+#include "string_stream.h"
+#include "socket_stream.h"
+#include "sys_net.h"
+
+#include "enum.h"
+#include "sxpr.h"
+
+#include "marshal.h"
+#include "connection.h"
+#include "select.h"
+#include "timer.h"
+#include "vcache.h"
+
+int create_socket(int socktype, uint32_t saddr, uint32_t port, int flags, Conn **val);
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/** Socket flags. */
+enum {
+ VSOCK_REUSE=1,
+ VSOCK_BIND=2,
+ VSOCK_CONNECT=4,
+ VSOCK_BROADCAST=8,
+ VSOCK_MULTICAST=16,
+ };
+
+#define PROGRAM "vnetd"
+#define VERSION "0.1"
+
+#define MODULE_NAME PROGRAM
+#define DEBUG
+#undef DEBUG
+#include "debug.h"
+
+#define OPT_PORT 'p'
+#define KEY_PORT "port"
+#define DOC_PORT "<port>\n\t" PROGRAM " UDP port (as a number or service name)"
+
+#define OPT_ADDR 'm'
+#define KEY_ADDR "mcaddr"
+#define DOC_ADDR "<address>\n\t" PROGRAM " multicast address"
+
+#define OPT_PEER 'r'
+#define KEY_PEER "peer"
+#define DOC_PEER "<peer>\n\t Peer " PROGRAM " to connect to (IP address or hostname)"
+
+#define OPT_FILE 'f'
+#define KEY_FILE "file"
+#define DOC_FILE "<file>\n\t Configuration file to load"
+
+#define OPT_CTRL 'c'
+#define KEY_CTRL "control"
+#define DOC_CTRL "<port>\n\t " PROGRAM " control port (as a number or service name)"
+
+#define OPT_HELP 'h'
+#define KEY_HELP "help"
+#define DOC_HELP "\n\tprint help"
+
+#define OPT_VERSION 'v'
+#define KEY_VERSION "version"
+#define DOC_VERSION "\n\tprint version"
+
+#define OPT_VERBOSE 'V'
+#define KEY_VERBOSE "verbose"
+#define DOC_VERBOSE "\n\tverbose flag"
+
+/** Print a usage message.
+ * Prints to stdout if err is zero, and exits with 0.
+ * Prints to stderr if err is non-zero, and exits with 1.
+ *
+ * @param err error code
+ */
+static void usage(int err){
+ FILE *out = (err ? stderr : stdout);
+
+ fprintf(out, "Usage: %s [options]\n", PROGRAM);
+ fprintf(out, "-%c, --%s %s\n", OPT_ADDR, KEY_ADDR, DOC_ADDR);
+ fprintf(out, "-%c, --%s %s\n", OPT_PORT, KEY_PORT, DOC_PORT);
+ fprintf(out, "-%c, --%s %s\n", OPT_PEER, KEY_PEER, DOC_PEER);
+ fprintf(out, "-%c, --%s %s\n", OPT_VERBOSE, KEY_VERBOSE, DOC_VERBOSE);
+ fprintf(out, "-%c, --%s %s\n", OPT_VERSION, KEY_VERSION, DOC_VERSION);
+ fprintf(out, "-%c, --%s %s\n", OPT_HELP, KEY_HELP, DOC_HELP);
+ exit(err ? 1 : 0);
+}
+
+/** Short options. Options followed by ':' take an argument. */
+static char *short_opts = (char[]){
+ OPT_ADDR, ':',
+ OPT_PORT, ':',
+ OPT_PEER, ':',
+ OPT_HELP,
+ OPT_VERSION,
+ OPT_VERBOSE,
+ 0 };
+
+/** Long options. */
+static struct option const long_opts[] = {
+ { KEY_ADDR, required_argument, NULL, OPT_ADDR },
+ { KEY_PORT, required_argument, NULL, OPT_PORT },
+ { KEY_PEER, required_argument, NULL, OPT_PEER },
+ { KEY_HELP, no_argument, NULL, OPT_HELP },
+ { KEY_VERSION, no_argument, NULL, OPT_VERSION },
+ { KEY_VERBOSE, no_argument, NULL, OPT_VERBOSE },
+ { NULL, 0, NULL, 0 }
+};
+
+/** Get address of vnetd. So we can ignore broadcast traffic
+ * we sent ourselves.
+ *
+ * @param addr
+ * @return 0 on success, error code otherwise
+ */
+int get_self_addr(struct sockaddr_in *addr){
+ int err = 0;
+ char hostname[1024] = {};
+ unsigned long saddr;
+
+ //dprintf(">\n");
+ err = gethostname(hostname, sizeof(hostname) -1);
+ if(err) goto exit;
+ err = get_host_address(hostname, &saddr);
+ if(err == 0){ err = -ENOENT; goto exit; }
+ err = 0;
+ addr->sin_addr.s_addr = saddr;
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Marshal a message.
+ *
+ * @param io destination
+ * @param msg message
+ * @return number of bytes written, or negative error code
+ */
+int VnetMsg_marshal(IOStream *io, VnetMsg *msg){
+ int err = 0;
+ int hdr_n = sizeof(VnetMsgHdr);
+
+ err = marshal_uint16(io, msg->hdr.id);
+ if(err < 0) goto exit;
+ err = marshal_uint16(io, msg->hdr.opcode);
+ if(err < 0) goto exit;
+ switch(msg->hdr.id){
+ case VNET_VARP_ID:
+ err = marshal_bytes(io, ((char*)msg) + hdr_n, sizeof(VarpHdr) - hdr_n);
+ break;
+ case VNET_FWD_ID:
+ err = marshal_uint16(io, msg->fwd.protocol);
+ if(err < 0) goto exit;
+ err = marshal_uint16(io, msg->fwd.len);
+ if(err < 0) goto exit;
+ err = marshal_bytes(io, msg->fwd.data, msg->fwd.len);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ exit:
+ return err;
+}
+
+/** Unmarshal a message.
+ *
+ * @param io source
+ * @param msg message to unmarshal into
+ * @return number of bytes read, or negative error code
+ */
+int VnetMsg_unmarshal(IOStream *io, VnetMsg *msg){
+ int err = 0;
+ int hdr_n = sizeof(VnetMsgHdr);
+
+ dprintf("> id\n");
+ err = unmarshal_uint16(io, &msg->hdr.id);
+ if(err < 0) goto exit;
+ dprintf("> opcode\n");
+ err = unmarshal_uint16(io, &msg->hdr.opcode);
+ if(err < 0) goto exit;
+ switch(msg->hdr.id){
+ case VNET_VARP_ID:
+ msg->hdr.opcode = htons(msg->hdr.opcode);
+ dprintf("> varp hdr_n=%d varphdr=%d\n", hdr_n, sizeof(VarpHdr));
+ err = unmarshal_bytes(io, ((char*)msg) + hdr_n, sizeof(VarpHdr) - hdr_n);
+ break;
+ case VNET_FWD_ID:
+ dprintf("> forward\n");
+ err = unmarshal_uint16(io, &msg->fwd.protocol);
+ if(err < 0) goto exit;
+ dprintf("> forward len\n");
+ err = unmarshal_uint16(io, &msg->fwd.len);
+ if(err < 0) goto exit;
+ dprintf("> forward bytes\n");
+ err = unmarshal_bytes(io, msg->fwd.data, msg->fwd.len);
+ break;
+ default:
+ wprintf("> Invalid id %d\n", msg->hdr.id);
+ err = -EINVAL;
+ break;
+ }
+ exit:
+ dprintf("< err=%d \n", err);
+ return err;
+}
+
+Vnetd _vnetd = {};
+Vnetd *vnetd = &_vnetd;
+
+/** Counter for timer alarms.
+ */
+static unsigned timer_alarms = 0;
+
+/** Set vnetd defaults.
+ *
+ * @param vnetd vnetd
+ */
+void vnetd_set_defaults(Vnetd *vnetd){
+ *vnetd = (Vnetd){};
+ vnetd->port = htons(VNETD_PORT);
+ vnetd->peer_port = vnetd->port; //htons(VNETD_PEER_PORT);
+ vnetd->verbose = FALSE;
+ vnetd->peers = ONULL;
+ vnetd->mcast_addr.sin_addr.s_addr = VARP_MCAST_ADDR;
+ vnetd->mcast_addr.sin_port = vnetd->port;
+}
+
+uint32_t vnetd_mcast_addr(Vnetd *vnetd){
+ return vnetd->mcast_addr.sin_addr.s_addr;
+}
+
+uint16_t vnetd_mcast_port(Vnetd *vnetd){
+ return vnetd->mcast_addr.sin_port;
+}
+
+/** Add a connection to a peer.
+ *
+ * @param vnetd vnetd
+ * @param conn connection
+ */
+void connections_add(Vnetd *vnetd, Conn *conn){
+ vnetd->connections = ConnList_add(conn, vnetd->connections);
+}
+
+/** Delete a connection to a peer.
+ *
+ * @param vnetd vnetd
+ * @param conn connection
+ */
+void connections_del(Vnetd *vnetd, Conn *conn){
+ ConnList *prev, *curr, *next;
+ for(prev = NULL, curr = vnetd->connections; curr; prev = curr, curr = next){
+ next = curr->next;
+ if(curr->conn == conn){
+ if(prev){
+ prev->next = curr->next;
+ } else {
+ vnetd->connections = curr->next;
+ }
+ }
+ }
+}
+
+/** Close all connections to peers.
+ *
+ * @param vnetd vnetd
+ */
+void connections_close_all(Vnetd *vnetd){
+ ConnList *l;
+ for(l = vnetd->connections; l; l = l->next){
+ Conn_close(l->conn);
+ }
+ vnetd->connections = NULL;
+}
+
+/** Add peer connections to a select set.
+ *
+ * @param vnetd vnetd
+ * @param set select set
+ */
+void connections_select(Vnetd *vnetd, SelectSet *set){
+ ConnList *l;
+ for(l = vnetd->connections; l; l = l->next){
+ SelectSet_add_read(set, l->conn->sock);
+ }
+}
+
+/** Handle peer connections according to a select set.
+ *
+ * @param vnetd vnetd
+ * @param set indicates ready connections
+ */
+void connections_handle(Vnetd *vnetd, SelectSet *set){
+ ConnList *prev, *curr, *next;
+ Conn *conn;
+ for(prev = NULL, curr = vnetd->connections; curr; prev = curr, curr = next){
+ next = curr->next;
+ conn = curr->conn;
+ if(FD_ISSET(conn->sock, &set->rd)){
+ int conn_err;
+ conn_err = Conn_handle(conn);
+ if(conn_err){
+ if(prev){
+ prev->next = curr->next;
+ } else {
+ vnetd->connections = curr->next;
+ }
+ }
+ }
+ }
+}
+
+/** Forward a message from a peer onto the local subnet.
+ *
+ * @param vnetd vnetd
+ * @param vmsg message
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_forward_local(Vnetd *vnetd, VnetMsg *vmsg){
+ int err = 0;
+ int sock = 0;
+ struct sockaddr_in addr_in;
+ struct sockaddr *addr = (struct sockaddr *)&addr_in;
+ socklen_t addr_n = sizeof(addr_in);
+
+ dprintf(">\n");
+ switch(vmsg->fwd.protocol){
+ case IPPROTO_ESP:
+ dprintf("> ESP\n");
+ sock = vnetd->esp_sock; break;
+ case IPPROTO_ETHERIP:
+ dprintf("> Etherip\n");
+ sock = vnetd->etherip_sock; break;
+ default:
+ err = -EINVAL;
+ goto exit;
+ }
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_addr = vnetd->mcast_addr.sin_addr;
+ addr_in.sin_port = htons(vmsg->fwd.protocol);
+ dprintf("> send dst=%s protocol=%d len=%d\n",
+ inet_ntoa(addr_in.sin_addr), vmsg->fwd.protocol, vmsg->fwd.len);
+ err = sendto(sock, vmsg->fwd.data, vmsg->fwd.len, 0, addr, addr_n);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Forward a message to a peer.
+ *
+ * @param conn peer connection
+ * @param protocol message protocol
+ * @param data message data
+ * @param data_n message size
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_forward_peer(Conn *conn, int protocol, void *data, int data_n){
+ int err = 0;
+ IOStream _io, *io = &_io;
+ StringData sdata;
+ char buf[1600];
+
+ dprintf("> addr=%s protocol=%d n=%d\n",
+ inet_ntoa(conn->addr.sin_addr), protocol, data_n);
+ string_stream_init(io, &sdata, buf, sizeof(buf));
+ dprintf("> 10\n");
+ err = marshal_uint16(io, VNET_FWD_ID);
+ if(err < 0) goto exit;
+ dprintf("> 20\n");
+ err = marshal_uint16(io, 0);
+ if(err < 0) goto exit;
+ dprintf("> 30\n");
+ err = marshal_uint16(io, protocol);
+ if(err < 0) goto exit;
+ dprintf("> 40\n");
+ err = marshal_uint16(io, data_n);
+ if(err < 0) goto exit;
+ dprintf("> 50\n");
+ err = marshal_bytes(io, data, data_n);
+ if(err < 0) goto exit;
+ dprintf("> 60 bytes=%d\n", IOStream_get_written(io));
+ err = IOStream_write(conn->out, buf, IOStream_get_written(io));
+ IOStream_flush(conn->out);
+ exit:
+ if(err < 0) perror(__FUNCTION__);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Forward a message to all peers.
+ *
+ * @param vnetd vnetd
+ * @param protocol message protocol
+ * @param data message data
+ * @param data_n message size
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_forward_peers(Vnetd *vnetd, int protocol, void *data, int data_n){
+ int err = 0;
+ ConnList *curr, *next;
+
+ dprintf(">\n");
+ for(curr = vnetd->connections; curr; curr = next){
+ next = curr->next;
+ vnetd_forward_peer(curr->conn, protocol, data, data_n);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Handler for a peer connection.
+ * Reads a VnetMsg from the connection and handles it.
+ *
+ * @param conn peer connection
+ * @return 0 on success, error code otherwise
+ */
+int conn_handle_fn(Conn *conn){
+ int err = 0;
+ VnetMsg *vmsg = ALLOCATE(VnetMsg);
+ IPMessage *msg = NULL;
+
+ dprintf("> addr=%s port=%u\n",
+ inet_ntoa(conn->addr.sin_addr),
+ ntohs(conn->addr.sin_port));
+ err = VnetMsg_unmarshal(conn->in, vmsg);
+ if(err < 0){
+ wprintf("> Unmarshal error %d\n", err);
+ goto exit;
+ }
+ switch(vmsg->hdr.id){
+ case VNET_VARP_ID:
+ dprintf("> Got varp message\n");
+ msg = ALLOCATE(IPMessage);
+ msg->conn = conn;
+ msg->saddr = conn->addr;
+ msg->data = vmsg;
+ err = vcache_handle_message(msg, 0);
+ err = 0;
+ break;
+ case VNET_FWD_ID:
+ dprintf("> Got forward message\n");
+ err = vnetd_forward_local(vnetd, vmsg);
+ err = 0;
+ break;
+ default:
+ wprintf("> Invalid id=%d\n", vmsg->hdr.id);
+ err = -EINVAL;
+ break;
+ }
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Accept an incoming tcp connection from a peer vnetd.
+ *
+ * @param sock tcp socket
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_accept(Vnetd *vnetd, Conn *conn){
+ Conn *new_conn = NULL;
+ struct sockaddr_in peer_in;
+ struct sockaddr *peer = (struct sockaddr *)&peer_in;
+ socklen_t peer_n = sizeof(peer_in);
+ int peersock;
+ int err = 0;
+
+ //dprintf(">\n");
+ new_conn = Conn_new(conn_handle_fn, vnetd);
+ //dprintf("> accept...\n");
+ peersock = accept(conn->sock, peer, &peer_n);
+ //dprintf("> accept=%d\n", peersock);
+ if(peersock < 0){
+ perror("accept");
+ err = -errno;
+ goto exit;
+ }
+ iprintf("> Accepted connection from %s:%d\n",
+ inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port));
+ err = Conn_init(new_conn, peersock, SOCK_STREAM, peer_in);
+ if(err) goto exit;
+ connections_add(vnetd, new_conn);
+ exit:
+ if(err){
+ Conn_close(new_conn);
+ }
+ if(err < 0) wprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Connect to a peer vnetd.
+ *
+ * @param vnetd vnetd
+ * @param addr address
+ * @param port port
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_connect(Vnetd *vnetd, struct in_addr addr, uint16_t port){
+ Conn *conn = NULL;
+ int err = 0;
+
+ //dprintf(">\n");
+ conn = Conn_new(conn_handle_fn, vnetd);
+ err = Conn_connect(conn, SOCK_STREAM, addr, port);
+ if(err) goto exit;
+ connections_add(vnetd, conn);
+ exit:
+ if(err){
+ Conn_close(conn);
+ }
+ //dprintf(" < err=%d\n", err);
+ return err;
+}
+
+/** Handle a message on the udp socket.
+ * Expecting to see VARP messages only.
+ *
+ * @param sock udp socket
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_handle_udp(Vnetd *vnetd, Conn *conn){
+ int err = 0, rcv = 0;
+ struct sockaddr_in self_in;
+ struct sockaddr_in peer_in;
+ struct sockaddr *peer = (struct sockaddr *)&peer_in;
+ socklen_t peer_n = sizeof(peer_in);
+ VnetMsg *vmsg = NULL;
+ void *data;
+ int data_n;
+ int flags = 0;
+ IPMessage *msg = NULL;
+
+ //dprintf(">\n");
+ self_in = vnetd->addr;
+ vmsg = ALLOCATE(VnetMsg);
+ data = &vmsg->varp.varph;
+ data_n = sizeof(VarpHdr);
+ rcv = recvfrom(conn->sock, data, data_n, flags, peer, &peer_n);
+ if(rcv < 0){
+ err = rcv;
+ goto exit;
+ }
+ dprintf("> Received %d bytes from %s:%d\n",
+ rcv, inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port));
+ if(rcv != data_n){
+ err = -EINVAL;
+ goto exit;
+ }
+ if(peer_in.sin_addr.s_addr == self_in.sin_addr.s_addr){
+ //dprintf("> Ignoring message from self.\n");
+ goto exit;
+ }
+ msg = ALLOCATE(IPMessage);
+ msg->conn = conn;
+ msg->saddr = peer_in;
+ msg->data = vmsg;
+
+ err = vcache_handle_message(msg, 1);
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Handle a message on a raw socket.
+ * Only deals with etherip and esp.
+ * Forwards messages to peers.
+ *
+ * @param vnetd vnetd
+ * @param sock socket
+ * @param protocol protocol
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_handle_protocol(Vnetd *vnetd, int sock, int protocol){
+ int err = 0, rcv = 0;
+ struct sockaddr_in self_in;
+ struct sockaddr_in peer_in;
+ struct sockaddr *peer = (struct sockaddr *)&peer_in;
+ socklen_t peer_n = sizeof(peer_in);
+ uint8_t buf[VNET_FWD_MAX];
+ int buf_n = sizeof(buf);
+ char *data, *end;
+ int flags = 0;
+ struct iphdr *iph = NULL;
+
+ //dprintf(">\n");
+ self_in = vnetd->addr;
+ rcv = recvfrom(sock, buf, buf_n, flags, peer, &peer_n);
+ if(rcv < 0){
+ err = rcv;
+ goto exit;
+ }
+ dprintf("> Received %d bytes from %s protocol=%d\n",
+ rcv, inet_ntoa(peer_in.sin_addr), protocol);
+ if(rcv < sizeof(struct iphdr)){
+ wprintf("> Message too short for IP header\n");
+ err = -EINVAL;
+ goto exit;
+ }
+ if(peer_in.sin_addr.s_addr == self_in.sin_addr.s_addr){
+ dprintf("> Ignoring message from self.\n");
+ goto exit;
+ }
+ data = buf;
+ end = buf + rcv;
+ iph = (void*)data;
+ data += (iph->ihl << 2);
+ vnetd_forward_peers(vnetd, protocol, data, end - data);
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Socket select loop.
+ * Accepts connections on the tcp socket and handles
+ * messages on the other sockets.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_select(Vnetd *vnetd){
+ int err = 0;
+ SelectSet set = {};
+ while(1){
+ SelectSet_zero(&set);
+ SelectSet_add_read(&set, vnetd->udp_conn->sock);
+ SelectSet_add_read(&set, vnetd->bcast_conn->sock);
+ SelectSet_add_read(&set, vnetd->etherip_sock);
+ SelectSet_add_read(&set, vnetd->esp_sock);
+ SelectSet_add_read(&set, vnetd->listen_conn->sock);
+ connections_select(vnetd, &set);
+ err = SelectSet_select(&set, NULL);
+ if(err == 0) continue;
+ if(err < 0){
+ if(errno == EINTR){
+ if(timer_alarms){
+ timer_alarms = 0;
+ process_timers();
+ }
+ continue;
+ }
+ perror("select");
+ goto exit;
+ }
+ if(FD_ISSET(vnetd->udp_conn->sock, &set.rd)){
+ vnetd_handle_udp(vnetd, vnetd->udp_conn);
+ }
+ if(FD_ISSET(vnetd->bcast_conn->sock, &set.rd)){
+ vnetd_handle_udp(vnetd, vnetd->bcast_conn);
+ }
+ if(FD_ISSET(vnetd->etherip_sock, &set.rd)){
+ vnetd_handle_protocol(vnetd, vnetd->etherip_sock, IPPROTO_ETHERIP);
+ }
+ if(FD_ISSET(vnetd->esp_sock, &set.rd)){
+ vnetd_handle_protocol(vnetd, vnetd->esp_sock, IPPROTO_ESP);
+ }
+ connections_handle(vnetd, &set);
+ if(FD_ISSET(vnetd->listen_conn->sock, &set.rd)){
+ vnetd_accept(vnetd, vnetd->listen_conn);
+ }
+ }
+ exit:
+ return err;
+}
+
+/** Set socket option to reuse address.
+ */
+int setsock_reuse(int sock, int reuse){
+ int err = 0;
+ err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
+ if(err < 0){
+ err = -errno;
+ perror("setsockopt SO_REUSEADDR");
+ }
+ return err;
+}
+
+/** Set socket broadcast option.
+ */
+int setsock_broadcast(int sock, int bcast){
+ int err = 0;
+ err = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast));
+ if(err < 0){
+ err = -errno;
+ perror("setsockopt SO_BROADCAST");
+ }
+ return err;
+}
+
+/** Join a socket to a multicast group.
+ */
+int setsock_multicast(int sock, uint32_t saddr){
+ int err = 0;
+ struct ip_mreqn mreq = {};
+ int mloop = 0;
+ // See 'man 7 ip' for these options.
+ mreq.imr_multiaddr.s_addr = saddr; // IP multicast address.
+ mreq.imr_address = vnetd->addr.sin_addr; // Interface IP address.
+ mreq.imr_ifindex = 0; // Interface index (0 means any).
+ err = setsockopt(sock, SOL_IP, IP_MULTICAST_LOOP, &mloop, sizeof(mloop));
+ if(err < 0){
+ err = -errno;
+ perror("setsockopt IP_MULTICAST_LOOP");
+ goto exit;
+ }
+ err = setsockopt(sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+ if(err < 0){
+ err = -errno;
+ perror("setsockopt IP_ADD_MEMBERSHIP");
+ goto exit;
+ }
+ exit:
+ return err;
+}
+
+/** Set a socket's multicast ttl (default is 1).
+ */
+int setsock_multicast_ttl(int sock, uint8_t ttl){
+ int err = 0;
+ err = setsockopt(sock, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
+ if(err < 0){
+ err = -errno;
+ perror("setsockopt IP_MULTICAST_TTL");
+ }
+ return err;
+}
+
+
+char * socket_flags(int flags){
+ static char s[6];
+ int i = 0;
+ s[i++] = (flags & VSOCK_CONNECT ? 'c' : '-');
+ s[i++] = (flags & VSOCK_BIND ? 'b' : '-');
+ s[i++] = (flags & VSOCK_REUSE ? 'r' : '-');
+ s[i++] = (flags & VSOCK_BROADCAST ? 'B' : '-');
+ s[i++] = (flags & VSOCK_MULTICAST ? 'M' : '-');
+ s[i++] = '\0';
+ return s;
+}
+
+/** Create a socket.
+ * The flags can include VSOCK_REUSE, VSOCK_BROADCAST, VSOCK_CONNECT.
+ *
+ * @param socktype socket type
+ * @param saddr address
+ * @param port port
+ * @param flags flags
+ * @param val return value for the socket connection
+ * @return 0 on success, error code otherwise
+ */
+int create_socket(int socktype, uint32_t saddr, uint32_t port, int flags, Conn **val){
+ int err = 0;
+ int sock = 0;
+ struct sockaddr_in addr_in;
+ struct sockaddr *addr = (struct sockaddr *)&addr_in;
+ socklen_t addr_n = sizeof(addr_in);
+ Conn *conn = NULL;
+ int reuse, bcast;
+
+ //dprintf(">\n");
+ reuse = (flags & VSOCK_REUSE);
+ bcast = (flags & VSOCK_BROADCAST);
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_addr.s_addr = saddr;
+ addr_in.sin_port = port;
+ dprintf("> flags=%s addr=%s port=%d\n", socket_flags(flags),
+ inet_ntoa(addr_in.sin_addr), ntohs(addr_in.sin_port));
+
+ sock = socket(AF_INET, socktype, 0);
+ if(sock < 0){
+ err = -errno;
+ goto exit;
+ }
+ if(reuse){
+ err = setsock_reuse(sock, reuse);
+ if(err < 0) goto exit;
+ }
+ if(bcast){
+ err = setsock_broadcast(sock, bcast);
+ if(err < 0) goto exit;
+ }
+ if(flags & VSOCK_MULTICAST){
+ err = setsock_multicast(sock, saddr);
+ if(err < 0) goto exit;
+ }
+ if(flags & VSOCK_CONNECT){
+ err = connect(sock, addr, addr_n);
+ if(err < 0){
+ err = -errno;
+ perror("connect");
+ goto exit;
+ }
+ }
+ if(flags & VSOCK_BIND){
+ err = bind(sock, addr, addr_n);
+ if(err < 0){
+ err = -errno;
+ perror("bind");
+ goto exit;
+ }
+ }
+ conn = Conn_new(NULL, NULL);
+ Conn_init(conn, sock, socktype, addr_in);
+ {
+ struct sockaddr_in self = {};
+ socklen_t self_n;
+ getsockname(conn->sock, (struct sockaddr *)&self, &self_n);
+ dprintf("> sockname sock=%d addr=%s port=%d\n",
+ conn->sock, inet_ntoa(self.sin_addr), ntohs(self.sin_port));
+ }
+ exit:
+ *val = (err ? NULL : conn);
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Create the tcp listen socket.
+ *
+ * @param vnetd program arguments
+ * @param val return value for the socket
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_listen_conn(Vnetd *vnetd, Conn **val){
+ int err = 0;
+ int flags = VSOCK_BIND | VSOCK_REUSE;
+ //dprintf(">\n");
+ err = create_socket(SOCK_STREAM, INADDR_ANY, vnetd->peer_port, flags, val);
+ if(err) goto exit;
+ err = listen((*val)->sock, 5);
+ if(err < 0){
+ err = -errno;
+ perror("listen");
+ goto exit;
+ }
+ exit:
+ if(err && *val){
+ Conn_close(*val);
+ *val = NULL;
+ }
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Create the udp socket.
+ *
+ * @param vnetd program arguments
+ * @param val return value for the socket
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_udp_conn(Vnetd *vnetd, Conn **val){
+ int err = 0;
+ uint32_t addr = INADDR_ANY;
+ uint16_t port = vnetd->port;
+ int flags = VSOCK_BIND | VSOCK_REUSE;
+ err = create_socket(SOCK_DGRAM, addr, port, flags, val);
+ return err;
+}
+
+/** Create the broadcast socket.
+ *
+ * @param vnetd program arguments
+ * @param val return value for the socket
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_broadcast_conn(Vnetd *vnetd, Conn **val){
+ int err = 0;
+ uint32_t addr = vnetd_mcast_addr(vnetd);
+ uint16_t port = vnetd_mcast_port(vnetd);
+ int flags = VSOCK_REUSE;
+ int multicast = IN_MULTICAST(ntohl(addr));
+
+ flags |= VSOCK_MULTICAST;
+ flags |= VSOCK_BROADCAST;
+
+ err = create_socket(SOCK_DGRAM, addr, port, flags, val);
+ if(err < 0) goto exit;
+ if(multicast){
+ err = setsock_multicast_ttl((*val)->sock, 1);
+ if(err < 0) goto exit;
+ }
+ if(0){
+ struct sockaddr * addr = (struct sockaddr *)&vnetd->addr;
+ socklen_t addr_n = sizeof(vnetd->addr);
+ dprintf("> sock=%d bind addr=%s:%d\n",
+ (*val)->sock, inet_ntoa(vnetd->addr.sin_addr), ntohs(vnetd->addr.sin_port));
+ err = bind((*val)->sock, addr, addr_n);
+ if(err < 0){
+ err = -errno;
+ perror("bind");
+ goto exit;
+ }
+ }
+ if(0){
+ struct sockaddr_in self = {};
+ socklen_t self_n;
+ getsockname((*val)->sock, (struct sockaddr *)&self, &self_n);
+ dprintf("> sockname sock=%d addr=%s port=%d\n",
+ (*val)->sock, inet_ntoa(self.sin_addr), ntohs(self.sin_port));
+ }
+ exit:
+ return err;
+}
+
+/** Type for signal handling functions. */
+typedef void SignalAction(int code, siginfo_t *info, void *data);
+
+/** Handle SIGCHLD by getting child exit status.
+ * This prevents child processes being defunct.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+static void sigaction_SIGCHLD(int code, siginfo_t *info, void *data){
+ int status;
+ pid_t pid;
+ pid = wait(&status);
+ dprintf("> child pid=%d status=%d\n", pid, status);
+}
+
+/** Handle SIGPIPE.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+static void sigaction_SIGPIPE(int code, siginfo_t *info, void *data){
+ dprintf("> SIGPIPE\n");
+}
+
+/** Handle SIGALRM.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+static void sigaction_SIGALRM(int code, siginfo_t *info, void *data){
+ //dprintf("> SIGALRM\n");
+ timer_alarms++;
+}
+
+/** Install a handler for a signal.
+ *
+ * @param signum signal
+ * @param action handler
+ * @return 0 on success, error code otherwise
+ */
+static int catch_signal(int signum, SignalAction *action){
+ int err = 0;
+ struct sigaction sig = {};
+ sig.sa_sigaction = action;
+ sig.sa_flags = SA_SIGINFO;
+ err = sigaction(signum, &sig, NULL);
+ if(err){
+ perror("sigaction");
+ }
+ return err;
+}
+
+/** Create a raw socket.
+ *
+ * @param protocol protocol
+ * @param flags flags
+ * @param sock return value for the socket
+ */
+int vnetd_raw_socket(int protocol, int flags, uint32_t mcaddr, int *sock){
+ int err;
+ int bcast = (flags & VSOCK_BROADCAST);
+ //dprintf("> protocol=%d\n", protocol);
+ err = *sock = socket(AF_INET, SOCK_RAW, protocol);
+ if(err < 0){
+ err = -errno;
+ perror("socket");
+ goto exit;
+ }
+ if(bcast){
+ err = setsock_broadcast(*sock, bcast);
+ if(err < 0) goto exit;
+ }
+ if(flags & VSOCK_MULTICAST){
+ err = setsock_multicast(*sock, mcaddr);
+ if(err < 0) goto exit;
+ }
+ exit:
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Connect to peer vnetds.
+ *
+ * @param vnetd vnetd
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_peers(Vnetd *vnetd){
+ int err =0;
+ Sxpr x, l;
+ struct in_addr addr = {};
+ for(l = vnetd->peers; CONSP(l); l = CDR(l)){
+ x = CAR(l);
+ addr.s_addr = OBJ_INT(x);
+ vnetd_connect(vnetd, addr, vnetd->peer_port);
+ }
+ return err;
+}
+
+/** Vnet daemon main program.
+ *
+ * @param vnetd program arguments
+ * @return 0 on success, error code otherwise
+ */
+int vnetd_main(Vnetd *vnetd){
+ int err = 0;
+
+ //dprintf(">\n");
+ err = get_self_addr(&vnetd->addr);
+ vnetd->addr.sin_port = vnetd->port;
+ iprintf("> VNETD\n");
+ iprintf("> addr=%s port=%u\n",
+ inet_ntoa(vnetd->addr.sin_addr), htons(vnetd->port));
+ iprintf("> mcaddr=%s port=%u\n",
+ inet_ntoa(vnetd->mcast_addr.sin_addr), htons(vnetd->port));
+ iprintf("> peers port=%u ", htons(vnetd->peer_port));
+ objprint(iostdout, vnetd->peers, 0); printf("\n");
+
+ err = vcache_init();
+ err = vnetd_peers(vnetd);
+
+ catch_signal(SIGCHLD,sigaction_SIGCHLD);
+ catch_signal(SIGPIPE,sigaction_SIGPIPE);
+ catch_signal(SIGALRM,sigaction_SIGALRM);
+ err = vnetd_listen_conn(vnetd, &vnetd->listen_conn);
+ if(err < 0) goto exit;
+ err = vnetd_udp_conn(vnetd, &vnetd->udp_conn);
+ if(err < 0) goto exit;
+ err = vnetd_broadcast_conn(vnetd, &vnetd->bcast_conn);
+ if(err < 0) goto exit;
+ {
+ int flags = VSOCK_BROADCAST | VSOCK_MULTICAST;
+ uint32_t mcaddr = vnetd->mcast_addr.sin_addr.s_addr;
+
+ err = vnetd_raw_socket(IPPROTO_ETHERIP, flags, mcaddr, &vnetd->etherip_sock);
+ if(err < 0) goto exit;
+ err = vnetd_raw_socket(IPPROTO_ESP, flags, mcaddr, &vnetd->esp_sock);
+ if(err < 0) goto exit;
+ }
+ err = vnetd_select(vnetd);
+ exit:
+ Conn_close(vnetd->listen_conn);
+ Conn_close(vnetd->udp_conn);
+ Conn_close(vnetd->bcast_conn);
+ connections_close_all(vnetd);
+ close(vnetd->etherip_sock);
+ close(vnetd->esp_sock);
+ //dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Parse command-line arguments and call the vnetd main program.
+ *
+ * @param arg argument count
+ * @param argv arguments
+ * @return 0 on success, 1 otherwise
+ */
+extern int main(int argc, char *argv[]){
+ int err = 0;
+ int key = 0;
+ int long_index = 0;
+
+ vnetd_set_defaults(vnetd);
+ while(1){
+ key = getopt_long(argc, argv, short_opts, long_opts, &long_index);
+ if(key == -1) break;
+ switch(key){
+ case OPT_ADDR:{
+ unsigned long addr;
+ err = get_host_address(optarg, &addr);
+ if(err) goto exit;
+ vnetd->mcast_addr.sin_addr.s_addr = addr;
+ break; }
+ case OPT_PORT:
+ err = convert_service_to_port(optarg, &vnetd->port);
+ if(err) goto exit;
+ break;
+ case OPT_PEER:{
+ unsigned long addr;
+ err = get_host_address(optarg, &addr);
+ if(err) goto exit;
+ //cons_push(&vnetd->peers, mkaddress(addr));
+ cons_push(&vnetd->peers, mkint(addr));
+ break; }
+ case OPT_HELP:
+ usage(0);
+ break;
+ case OPT_VERBOSE:
+ vnetd->verbose = TRUE;
+ break;
+ case OPT_VERSION:
+ iprintf("> %s %s\n", PROGRAM, VERSION);
+ exit(0);
+ break;
+ default:
+ usage(EINVAL);
+ break;
+ }
+ }
+ err = vnetd_main(vnetd);
+ exit:
+ if(err && key > 0){
+ eprintf("> Error in arg %c\n", key);
+ }
+ return (err ? 1 : 0);
+}
diff --git a/tools/vnet/vnetd/vnetd.h b/tools/vnet/vnetd/vnetd.h
new file mode 100644
index 0000000000..757b003b50
--- /dev/null
+++ b/tools/vnet/vnetd/vnetd.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or (at your option) any later version. This library is
+ * distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _VNET_VNETD_H_
+#define _VNET_VNETD_H_
+
+#include <asm/types.h>
+#include <linux/if_ether.h>
+#include "if_varp.h"
+
+#include "connection.h"
+#include "sxpr.h"
+
+/** Vnetd udp port in host order. */
+#define VNETD_PORT VARP_PORT
+
+/** Vnetd peer port in host order. */
+#define VNETD_PEER_PORT (VARP_PORT + 1)
+
+typedef struct VnetMsgVarp {
+ VarpHdr varph;
+} VnetMsgVarp;
+
+#define VNET_FWD_MAX (1500 + 200)
+
+typedef struct VnetMsgFwd {
+ VnetMsgHdr;
+ uint16_t protocol;
+ uint16_t len;
+ uint8_t data[VNET_FWD_MAX];
+} __attribute__((packed)) VnetMsgFwd;
+
+typedef union VnetMsg {
+ VnetMsgHdr hdr;
+ VnetMsgVarp varp;
+ VnetMsgFwd fwd;
+} VnetMsg;
+
+enum {
+ VNET_VARP_ID = VARP_ID,
+ VNET_FWD_ID = 200,
+};
+
+typedef struct Vnetd {
+ unsigned long port;
+ unsigned long peer_port;
+ int verbose;
+
+ int esp_sock;
+ int etherip_sock;
+
+ struct sockaddr_in addr;
+ struct sockaddr_in mcast_addr;
+
+ Sxpr peers;
+
+ Conn *listen_conn;
+ Conn *udp_conn;
+ Conn *bcast_conn;
+
+ ConnList *connections;
+
+} Vnetd;
+
+extern Vnetd *vnetd;
+
+#endif /* ! _VNET_VNETD_H_ */
diff --git a/tools/x2d2/Makefile b/tools/x2d2/Makefile
new file mode 100644
index 0000000000..43f6964cae
--- /dev/null
+++ b/tools/x2d2/Makefile
@@ -0,0 +1,22 @@
+XEN_ROOT=../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+CC = gcc
+CFLAGS += -Wall -Werror -O3 -fno-strict-aliasing
+
+CFLAGS += -I $(XEN_XC)
+CFLAGS += -I $(XEN_LIBXC)
+CFLAGS += -I $(XEN_LIBXUTIL)
+
+HDRS = $(wildcard *.h)
+OBJS = $(patsubst %.c,%.o,$(wildcard *.c))
+
+BIN = minixend
+
+all: $(BIN)
+
+clean:
+ $(RM) *.a *.so *.o *.rpm $(BIN)
+
+$(BIN): $(OBJS)
+ $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -lxc -lxutil -lpthread
diff --git a/tools/x2d2/cntrl_con.c b/tools/x2d2/cntrl_con.c
new file mode 100644
index 0000000000..46084dbdee
--- /dev/null
+++ b/tools/x2d2/cntrl_con.c
@@ -0,0 +1,457 @@
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <pthread.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "minixend.h"
+
+struct command {
+ const char *name;
+ void (*func)(struct open_connection *oc, const struct command *c,
+ const char *, const char *);
+};
+
+static void
+domain_created(const char *name, int mem_kb, int domid)
+{
+ struct domain *d;
+ d = xmalloc(sizeof(*d));
+ d->domid = domid;
+ d->name = xstrdup(name);
+ d->mem_kb = mem_kb;
+ d->state = DOM_STATE_CREATED;
+ d->control_evtchn = -1; /* Not connected yet. */
+
+ memcpy(d->netif_mac, "\xaa\x00\x00\x02\x00\x00", 6);
+ d->netif_mac[5] = d->domid;
+
+ pthread_mutex_init(&d->mux, NULL);
+ pthread_cond_init(&d->cond, NULL);
+ pthread_create(&d->thread, NULL, domain_thread_func, d);
+
+ list_insert_after(&d->domain_list, &head_domain);
+}
+
+static struct domain *
+find_domain(int domain_id)
+{
+ struct domain *d;
+
+ foreach_domain(d) {
+ if (d->domid == domain_id)
+ return d;
+ }
+ return NULL;
+}
+
+static int
+free_event_port(struct domain *d, int port)
+{
+ if (d == NULL)
+ return xc_evtchn_close(xc_handle, DOMID_SELF, port);
+ else
+ return xc_evtchn_close(xc_handle, d->domid, port);
+}
+
+static char *
+readline(struct open_connection *oc)
+{
+ char *end;
+ char *res;
+ int line_length;
+
+ if (oc->state == OC_STATE_ERROR)
+ return NULL;
+
+ end = memchr(oc->buf, '\r', oc->buf_used);
+ assert(end != NULL);
+ line_length = end - oc->buf;
+
+ res = xmalloc(line_length + 1);
+ memcpy(res, oc->buf, line_length);
+ res[line_length] = 0;
+ memmove(oc->buf, oc->buf + line_length + 2,
+ oc->buf_used - line_length - 2);
+
+ oc->buf_used -= line_length + 2;
+
+ if (memchr(oc->buf, '\n', oc->buf_used))
+ oc->state = OC_STATE_COMMAND_PENDING;
+ else
+ oc->state = OC_STATE_CONNECTED;
+
+ return res;
+}
+
+static unsigned long
+find_domain_shared_info_mfn(struct domain *d)
+{
+ xc_dominfo_t info;
+
+ xc_domain_getinfo(xc_handle, d->domid, 1, &info);
+ return info.shared_info_frame;
+}
+
+static void
+send_message(struct open_connection *oc, const char *fmt, ...)
+{
+ char *buf;
+ va_list ap;
+ int size;
+ int off;
+ ssize_t r;
+
+ if (oc->state == OC_STATE_ERROR)
+ return;
+
+ va_start(ap, fmt);
+ size = vasprintf(&buf, fmt, ap);
+ va_end(ap);
+ if (size < 0)
+ err(1, "preparing response to a query");
+ assert(buf[0] == 'E' || buf[0] == 'N');
+ assert(isdigit(buf[1]));
+ assert(isdigit(buf[2]));
+ assert(buf[3] == ' ' || buf[3] == '\n');
+
+ off = 0;
+ while (off < size) {
+ r = write(oc->fd, buf + off, size - off);
+ if (r < 0) {
+ warn("sending response to remote");
+ oc->state = OC_STATE_ERROR;
+ free(buf);
+ return;
+ }
+ off += r;
+ }
+ free(buf);
+}
+
+static void
+default_command_handler(struct open_connection *oc, const struct command *ign,
+ const char *buf, const char *args)
+{
+ warnx("bad command %s", buf);
+ send_message(oc, "E00 unknown command %s\n", buf);
+}
+
+static void
+create_command_handler(struct open_connection *oc, const struct command *ign,
+ const char *buf, const char *args)
+{
+ char *name;
+ unsigned mem_kb;
+ int r;
+ u32 domid = -1;
+
+ r = sscanf(args, "%d %a[^\n]", &mem_kb, &name);
+ if (r != 2) {
+ send_message(oc, "E01 failed to parse %s\n", args);
+ return;
+ }
+ r = xc_domain_create(xc_handle, mem_kb, -1, 0, &domid);
+ if (r < 0) {
+ send_message(oc, "E02 creating domain (%s)\n",
+ strerror(errno));
+ free(name);
+ return;
+ }
+
+ domain_created(name, mem_kb, domid);
+
+ send_message(oc, "N00 %d\n", domid);
+ free(name);
+}
+
+static void
+build_command_handler(struct open_connection *oc, const struct command *ign,
+ const char *buf, const char *args)
+{
+ struct domain *d;
+ int domain_id;
+ char *image, *cmdline;
+ int event_ports[2];
+ int r;
+
+ r = sscanf(args, "%d %a[^\t] %a[^\n]", &domain_id,
+ &image, &cmdline);
+ if (r != 3) {
+ send_message(oc, "E03 failed to parse %s\n", args);
+ return;
+ }
+ d = find_domain(domain_id);
+ if (d == NULL) {
+ send_message(oc, "E04 unknown domain %d\n", domain_id);
+ goto out;
+ }
+ if (d->state != DOM_STATE_CREATED) {
+ send_message(oc, "E05 domain %d in bad state\n", domain_id);
+ goto out;
+ }
+
+ r = allocate_event_channel(d, event_ports);
+ if (r < 0) {
+ send_message(oc, "E06 allocating control event channel: %s\n",
+ strerror(errno));
+ goto out;
+ }
+
+ r = xc_linux_build(xc_handle, domain_id, image, NULL, cmdline,
+ event_ports[1], 0);
+ if (r < 0) {
+ send_message(oc, "E07 building domain: %s\n",
+ strerror(errno));
+ free_event_port(NULL, event_ports[0]);
+ free_event_port(d, event_ports[1]);
+ goto out;
+ }
+
+ if (ioctl(evtchn_fd, EVTCHN_BIND, event_ports[0]) < 0)
+ err(1, "binding to event control event channel");
+
+ d->shared_info_mfn = find_domain_shared_info_mfn(d);
+ d->shared_info = map_domain_mem(d, d->shared_info_mfn);
+ if (d->shared_info == NULL)
+ err(1, "maping domain shared info page at %lx.\n",
+ d->shared_info_mfn);
+ d->ctrl_if = (control_if_t *)((unsigned)d->shared_info + 2048);
+
+ d->control_evtchn = event_ports[0];
+ d->state = DOM_STATE_PAUSED;
+
+ send_message(oc, "N00\n");
+
+ out:
+ free(image);
+ free(cmdline);
+ return;
+}
+
+static void
+unpause_command_handler(struct open_connection *oc,
+ const struct command *ign,
+ const char *buf,
+ const char *args)
+{
+ int domain_id;
+ int r;
+ struct domain *d;
+
+ r = sscanf(args, "%d", &domain_id);
+ if (r != 1) {
+ send_message(oc, "E08 cannot parse %s\n", args);
+ return;
+ }
+ d = find_domain(domain_id);
+ if (d == NULL) {
+ send_message(oc, "E09 cannot find domain %d\n", domain_id);
+ return;
+ }
+ if (d->state != DOM_STATE_PAUSED) {
+ send_message(oc, "E10 domain not paused\n");
+ return;
+ }
+
+ r = xc_domain_unpause(xc_handle, d->domid);
+ if (r < 0) {
+ send_message(oc, "E11 unpausing domain: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ d->state = DOM_STATE_RUNNING;
+ send_message(oc, "N00\n");
+}
+
+static void
+console_command_handler(struct open_connection *oc,
+ const struct command *ign,
+ const char *buf,
+ const char *args)
+{
+ int domain_id;
+ struct domain *d;
+ int r;
+ struct sockaddr_in name;
+ socklen_t namelen;
+
+ r = sscanf(args, "%d", &domain_id);
+ if (r != 1) {
+ send_message(oc, "E12 cannot parse %s\n", args);
+ return;
+ }
+ d = find_domain(domain_id);
+ if (d == NULL) {
+ send_message(oc, "E13 cannot find domain %d\n", domain_id);
+ return;
+ }
+ if (d->cc != NULL) {
+ send_message(oc, "E14 console already exists\n");
+ return;
+ }
+
+ d->cc = xmalloc(sizeof(*d->cc));
+ d->cc->fd = socket(PF_INET, SOCK_STREAM, 0);
+ if (d->cc->fd < 0)
+ err(1, "creating console socket");
+ d->cc->dom = d;
+ d->cc->state = CC_STATE_PENDING;
+ d->cc->buf_used = 0;
+ d->cc->buf_allocated = 0;
+ d->cc->buf = NULL;
+
+ r = listen(d->cc->fd, 1);
+ if (r < 0)
+ err(1, "listening on console socket");
+ namelen = sizeof(name);
+ r = getsockname(d->cc->fd, (struct sockaddr *)&name, &namelen);
+ if (r < 0)
+ err(1, "getting name of console socket");
+ assert(name.sin_family == AF_INET);
+ assert(namelen == sizeof(name));
+ list_insert_after(&d->cc->list, &head_console);
+ send_message(oc, "N00 %d\n", ntohs(name.sin_port));
+}
+
+static void
+plug_command_handler(struct open_connection *oc,
+ const struct command *ign,
+ const char *buf,
+ const char *args)
+{
+ unsigned domid;
+ int r;
+ struct domain *d;
+
+ r = sscanf(args, "%d", &domid);
+ if (r != 1) {
+ send_message(oc, "E15 cannot parse %s\n", args);
+ return;
+ }
+ d = find_domain(domid);
+ if (d == NULL) {
+ send_message(oc, "E16 cannot find domain %d\n", domid);
+ return;
+ }
+
+ d->plugged = 1;
+ send_message(oc, "N00\n");
+ PRINTF(1, "set domain %d plug state to %d\n", d->domid, d->plugged);
+}
+
+static void
+destroy_command_handler(struct open_connection *oc,
+ const struct command *ign,
+ const char *buf,
+ const char *args)
+{
+ unsigned domid;
+ int r;
+ struct domain *d;
+
+ r = sscanf(args, "%d", &domid);
+ if (r != 1) {
+ send_message(oc, "E17 cannot parse %s\n", args);
+ return;
+ }
+ d = find_domain(domid);
+ if (d == NULL) {
+ send_message(oc, "E18 cannot find domain %d\n", domid);
+ return;
+ }
+
+ r = xc_domain_destroy(xc_handle, domid);
+ if (r < 0) {
+ send_message( oc, "E19 error destroying domain %d: %s\n",
+ domid, strerror(errno) );
+ return;
+ }
+ d->state = DOM_STATE_DEAD;
+
+ send_message(oc, "N00\n");
+}
+
+static void
+list_command_handler(struct open_connection *oc,
+ const struct command *ign,
+ const char *buf,
+ const char *args)
+{
+ struct domain *d;
+ static const char *const state_strings[] = {
+ [DOM_STATE_CREATED] = "created",
+ [DOM_STATE_PAUSED] = "paused",
+ [DOM_STATE_RUNNING] = "running",
+ [DOM_STATE_DEAD] = "dead"
+ };
+
+ foreach_domain(d) {
+ send_message(oc, "N01 %d %s %d %s\n",
+ d->domid,
+ d->name,
+ d->mem_kb,
+ state_strings[d->state]);
+ }
+ send_message(oc, "N00\n");
+}
+
+static struct command
+default_command = { NULL, default_command_handler };
+
+static struct command
+commands[] = {
+ { "build", build_command_handler },
+ { "console", console_command_handler },
+ { "create", create_command_handler },
+ { "destroy", destroy_command_handler },
+ { "plug", plug_command_handler },
+ { "list", list_command_handler },
+ { "unpause", unpause_command_handler }
+};
+
+void
+process_command(struct open_connection *oc)
+{
+ char *buf, *b;
+ int command_len;
+ int x;
+ struct command *cmd;
+
+ buf = readline(oc);
+ if (buf == NULL)
+ return;
+ b = strchr(buf, ' ');
+ if (b == NULL)
+ command_len = strlen(buf);
+ else
+ command_len = b - buf;
+ b = buf + command_len;
+ while (b[0] && b[0] == ' ')
+ b++;
+
+ cmd = &default_command;
+ for (x = 0; x < sizeof(commands) / sizeof(commands[0]); x++) {
+ if (strlen(commands[x].name) == command_len &&
+ memcmp(commands[x].name, buf, command_len) == 0) {
+ cmd = &commands[x];
+ break;
+ }
+ }
+ cmd->func(oc, cmd, buf, b);
+ free(buf);
+ return;
+}
diff --git a/tools/x2d2/minixend.c b/tools/x2d2/minixend.c
new file mode 100644
index 0000000000..64fe27195a
--- /dev/null
+++ b/tools/x2d2/minixend.c
@@ -0,0 +1,939 @@
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <asm/page.h>
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <printf.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "minixend.h"
+
+#define NETWORK_SCRIPT "/etc/xen/scripts/network"
+#define VIFBRIDGE_SCRIPT "/etc/xen/scripts/vif-bridge"
+
+#define MINIXEND_PORT 5123
+
+#define mb() asm volatile ("" ::: "memory")
+
+static void send_control_message(int type, int subtype, int id,
+ int size, void *payload,
+ struct domain *target);
+
+struct list_head
+head_domain = LIST_HEAD(&head_domain);
+
+static struct list_head
+head_connection = LIST_HEAD(&head_connection);
+
+struct list_head
+head_console = LIST_HEAD(&head_console);
+
+#define foreach_open_connection(d) \
+foreach_item(d, &head_connection, struct open_connection, connection_list)
+
+/* Not modified after initial start up */
+static struct domain *dom0;
+unsigned xc_handle;
+static int listen_fd;
+int evtchn_fd;
+
+static struct list_head
+head_event_receiver = LIST_HEAD(&head_event_receiver);
+
+struct event_receiver {
+ struct list_head list;
+ int id;
+ pthread_cond_t cond;
+};
+
+/* We're protected by the dom0 mutex in here */
+static struct event_receiver *
+allocate_event_receiver(struct domain *d)
+{
+ static int next_message_id;
+ struct event_receiver *work;
+
+ assert(d == dom0);
+ work = xmalloc(sizeof(*work));
+ work->id = next_message_id++;
+ pthread_cond_init(&work->cond, NULL);
+
+ list_insert_after(&work->list, &head_event_receiver);
+
+ return work;
+}
+
+static struct event_receiver *
+find_event_receiver(int id)
+{
+ struct event_receiver *work;
+ foreach_item(work, &head_event_receiver, struct event_receiver, list)
+ if (work->id == id)
+ return work;
+ return NULL;
+}
+
+static void
+release_event_receiver(struct event_receiver *w)
+{
+ list_remove(&w->list);
+ pthread_cond_destroy(&w->cond);
+ free(w);
+}
+
+/* Send a message to dom0, and then block awaiting a reply. */
+/* Make sure we don't hold any domain mutexs */
+static void
+send_dom0_message_block(control_msg_t *msg)
+{
+ CONTROL_RING_IDX c;
+ struct event_receiver *er;
+ control_msg_t buf;
+
+ PRINTF(0, "sending message to dom0 and blocking for reply.\n");
+ pthread_mutex_lock(&dom0->mux);
+ PRINTF(0, "got dom0 lock.\n");
+ er = allocate_event_receiver(dom0);
+ PRINTF(0, "allocated evetn receiver.\n");
+ msg->id = er->id;
+ PRINTF(1, "sending message with id %d\n", msg->id);
+ send_control_message(msg->type, msg->subtype,
+ msg->id, msg->length, msg->msg, dom0);
+ xc_evtchn_send(xc_handle, dom0->control_evtchn);
+
+ PRINTF(0, "waiting for reply\n");
+ pthread_cond_wait(&er->cond, &dom0->mux);
+ PRINTF(0, "got reply\n");
+
+ c = dom0->rx_resp_cons % CONTROL_RING_SIZE;
+ memcpy(&buf, &dom0->ctrl_if->rx_ring[c], sizeof(buf));
+ assert(msg->id == buf.id);
+ assert(msg->type == buf.type);
+ assert(msg->subtype == buf.subtype);
+ memcpy(msg, &buf, sizeof(*msg));
+ dom0->rx_resp_cons++;
+
+ release_event_receiver(er);
+
+ pthread_mutex_unlock(&dom0->mux);
+
+ PRINTF(1, "got reply to message with id %d\n", msg->id);
+}
+
+/* Allocate an interdomain event channel. event_ports[0] is the
+ local event port number, event_ports[1] the remote */
+int
+allocate_event_channel(struct domain *d, int event_ports[2])
+{
+ return xc_evtchn_bind_interdomain(xc_handle, DOMID_SELF,
+ d->domid, event_ports,
+ event_ports+1);
+}
+
+static void
+accept_new_connection(void)
+{
+ int fd;
+ struct open_connection *oc;
+
+ fd = accept(listen_fd, NULL, NULL);
+ if (fd < 0)
+ return;
+ oc = xmalloc(sizeof(*oc));
+ oc->fd = fd;
+ oc->state = OC_STATE_CONNECTED;
+ oc->buf_used = 0;
+ oc->buf_allocated = 16;
+ oc->buf = xmalloc(oc->buf_allocated);
+ list_insert_after(&oc->connection_list, &head_connection);
+}
+
+static void
+closedown_connection(struct open_connection *oc)
+{
+ close(oc->fd);
+ assert(oc->buf);
+ free(oc->buf);
+ free(oc);
+}
+
+#if 0
+/* Hackl for the benefit of domain replay */
+static unsigned
+report_work(u32 *ptr, u32 val, unsigned dom, int do_direct)
+{
+ if (!do_direct) {
+ int rc;
+ asm("int $0x80" : "=a" (rc)
+ : "0" (264), "b" (ptr), "c" (val), "d" (dom));
+ if (rc < 0) {
+ errno = -rc;
+ rc = -1;
+ }
+ return rc;
+ } else {
+ *ptr = val;
+ return 0;
+ }
+}
+#else
+static unsigned
+report_work(u32 *ptr, u32 val, unsigned dom, int do_direct)
+{
+ *ptr = val;
+ return 0;
+}
+#endif
+
+static void
+send_control_reply(const control_msg_t *msg, struct domain *d)
+{
+ CONTROL_RING_IDX c;
+
+ PRINTF(3,"Control reply, type %d:%d, length %d.\n",
+ msg->type, msg->subtype, msg->length);
+ c = d->ctrl_if->tx_resp_prod % CONTROL_RING_SIZE;
+ memcpy(&d->ctrl_if->tx_ring[c], msg, sizeof(*msg));
+ report_work(&d->ctrl_if->tx_resp_prod,
+ d->ctrl_if->tx_resp_prod + 1,
+ d->domid,
+ 0);
+ PRINTF(4,"tx_resp_prod %ld.\n", d->ctrl_if->tx_resp_prod);
+ assert(!d->plugged);
+}
+
+static void
+send_trivial_control_reply(const control_msg_t *msg, struct domain *d)
+{
+ control_msg_t rep;
+
+ memset(&rep, 0, sizeof(rep));
+ rep.type = msg->type;
+ rep.subtype = msg->subtype;
+ rep.id = msg->id;
+ send_control_reply(&rep, d);
+}
+
+static void
+process_console_control_message(control_msg_t *m, struct domain *d)
+{
+ int off;
+ int r;
+
+ if (m->subtype != CMSG_CONSOLE_DATA) {
+ warnx("unknown console message subtype %d",
+ m->subtype);
+ return;
+ }
+
+ if (m->length > 60) {
+ warnx("truncating message from domain %d (was length %d)",
+ d->domid, m->length);
+ m->length = 60;
+ }
+ PRINTF(1, "DOM%d: %.*s\n", d->domid, m->length, m->msg);
+ send_trivial_control_reply(m, d);
+
+ if (d->cc) {
+ PRINTF(5, "Have a console connection.\n");
+ if (d->cc->state == CC_STATE_CONNECTED) {
+ PRINTF(5, "Console is connected, sending directly.\n");
+ for (off = 0; off < m->length; off += r) {
+ r = write(d->cc->fd, m->msg + off,
+ m->length - off);
+ if (r <= 0) {
+ d->cc->state = CC_STATE_ERROR;
+ break;
+ }
+ }
+ } else {
+ PRINTF(5, "Console not connected, buffering.\n");
+ if (d->cc->buf_allocated == 0) {
+ d->cc->buf_allocated = 60;
+ d->cc->buf = xmalloc(d->cc->buf_allocated);
+ d->cc->buf_used = 0;
+ } else if (d->cc->buf_allocated <
+ d->cc->buf_used + m->length) {
+ d->cc->buf_allocated += 60;
+ d->cc->buf = xrealloc(d->cc->buf,
+ d->cc->buf_allocated);
+ }
+ assert(d->cc->buf_allocated >=
+ d->cc->buf_used + m->length);
+ memcpy(d->cc->buf + d->cc->buf_used,
+ m->msg,
+ m->length);
+ d->cc->buf_used += m->length;
+ }
+ }
+}
+
+static void
+process_blkif_fe_message(control_msg_t *m, struct domain *d)
+{
+ switch (m->subtype) {
+ default:
+ warnx("unknown blkif front end message subtype %d",
+ m->subtype);
+ }
+}
+
+static void
+send_control_message(int type, int subtype, int id,
+ int size, void *payload, struct domain *target)
+{
+ control_msg_t msg;
+ CONTROL_RING_IDX c;
+
+ msg.type = type;
+ msg.subtype = subtype;
+ msg.id = id;
+ msg.length = size;
+ memcpy(msg.msg, payload, size);
+
+ c = target->ctrl_if->rx_req_prod % CONTROL_RING_SIZE;
+ memcpy(&target->ctrl_if->rx_ring[c], &msg, sizeof(msg));
+ report_work(&target->ctrl_if->rx_req_prod,
+ target->ctrl_if->rx_req_prod + 1,
+ target->domid,
+ 0);
+ assert(!target->plugged);
+}
+
+/* Procedure for bringing a new netif front end up:
+
+ -- Front end sends us NETIF_FE_DRIVER_STATUS_CHANGED
+ -- We send back end NETIF_BE_CREATE, wait for a reply
+ -- Back end creates a new netif for us, replies
+ -- We send front end a NETIF_FE_DRIVER_STATUS_CHANGED message saying
+ how many interfaces we've created for it
+ -- We send front end a NETIF_FE_INTERFACE_STATUS_CHANGED for each
+ netif created
+ -- Front end sends us a NETIF_FE_INTERFACE_CONNECT for each netif
+*/
+static void
+handle_netif_fe_driver_status(control_msg_t *m,
+ netif_fe_driver_status_t *sh,
+ struct domain *d)
+{
+ netif_fe_interface_status_t if_s;
+ control_msg_t be_msg;
+ netif_be_create_t *be = (void *)be_msg.msg;
+ int r;
+
+ switch (sh->status) {
+ case NETIF_DRIVER_STATUS_UP:
+ /* Tell the back end about the new interface coming
+ * up. */
+ if (d->created_netif_backend) {
+ send_control_reply(m, d);
+ send_control_message(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_DRIVER_STATUS,
+ 1,
+ sizeof(*sh),
+ sh,
+ d);
+ return;
+ }
+ be_msg.type = CMSG_NETIF_BE;
+ be_msg.subtype = CMSG_NETIF_BE_CREATE;
+ be_msg.id = d->domid;
+ be_msg.length = sizeof(*be);
+ be->domid = d->domid;
+ be->netif_handle = 0;
+ memcpy(be->mac, d->netif_mac, 6);
+
+ PRINTF(2,"Telling back end about new front end.\n");
+ pthread_mutex_unlock(&d->mux);
+ send_dom0_message_block(&be_msg);
+ pthread_mutex_lock(&d->mux);
+ PRINTF(3,"Done.\n");
+
+ if (be->status != NETIF_BE_STATUS_OKAY) {
+ /* Uh oh... can't bring back end
+ * up. */
+ send_control_reply(m, d);
+ send_control_message(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_DRIVER_STATUS,
+ 1,
+ sizeof(*sh),
+ sh,
+ d);
+ return;
+ }
+ d->created_netif_backend = 1;
+
+ r = our_system(VIFBRIDGE_SCRIPT " up domain=%s mac=%.02x:%.02x:%.02x:%.02x:%.02x:%.02x vif=vif%d.0 bridge=xen-br0",
+ d->name,
+ d->netif_mac[0],
+ d->netif_mac[1],
+ d->netif_mac[2],
+ d->netif_mac[3],
+ d->netif_mac[4],
+ d->netif_mac[5],
+ d->domid);
+ if (r != 0)
+ warn("error %d running " VIFBRIDGE_SCRIPT, r);
+
+ /* Tell domain how many interfaces it has to deal
+ * with. */
+ send_control_reply(m, d);
+ send_control_message(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_DRIVER_STATUS,
+ 1,
+ sizeof(*sh),
+ sh,
+ d);
+
+ PRINTF(2,"Telling front end about its interfaces.\n");
+ if_s.handle = 0;
+ if_s.status = NETIF_INTERFACE_STATUS_DISCONNECTED;
+ send_control_message(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_INTERFACE_STATUS,
+ 1,
+ sizeof(if_s),
+ &if_s,
+ d);
+ PRINTF(3,"Done.\n");
+
+ break;
+ default:
+ warnx("unknown netif status %ld", sh->status);
+ break;
+ }
+}
+
+static void
+handle_netif_fe_interface_connect(control_msg_t *m,
+ netif_fe_interface_connect_t *ic,
+ struct domain *d)
+{
+ control_msg_t be_msg;
+ netif_be_connect_t *bmsg = (void *)be_msg.msg;
+ netif_fe_interface_status_t fmsg = {0};
+ int evtchn_ports[2];
+ int r;
+
+ PRINTF(4, "front end sent us an interface connect message.\n");
+ send_trivial_control_reply(m, d);
+
+ r = xc_evtchn_bind_interdomain(xc_handle,
+ dom0->domid,
+ d->domid,
+ &evtchn_ports[0],
+ &evtchn_ports[1]);
+ if (r < 0)
+ err(1, "allocating network event channel");
+
+ be_msg.type = CMSG_NETIF_BE;
+ be_msg.subtype = CMSG_NETIF_BE_CONNECT;
+ be_msg.id = 0;
+ be_msg.length = sizeof(*bmsg);
+ bmsg->domid = d->domid;
+ bmsg->netif_handle = ic->handle;
+ bmsg->tx_shmem_frame = ic->tx_shmem_frame;
+ bmsg->rx_shmem_frame = ic->rx_shmem_frame;
+ bmsg->evtchn = evtchn_ports[0];
+
+ pthread_mutex_unlock(&d->mux);
+ send_dom0_message_block(&be_msg);
+ pthread_mutex_lock(&d->mux);
+
+ if (bmsg->status != NETIF_BE_STATUS_OKAY) {
+ PRINTF(2, "error connected backend netif: %ld\n",
+ bmsg->status);
+ abort(); /* Need to handle this */
+ } else {
+ PRINTF(3, "connect backend netif\n");
+
+ /* Tell the domain that we've connected it up. */
+ fmsg.handle = ic->handle;
+ fmsg.status = NETIF_INTERFACE_STATUS_CONNECTED;
+ fmsg.evtchn = evtchn_ports[1];
+ memcpy(fmsg.mac, d->netif_mac, 6);
+
+ send_control_message(CMSG_NETIF_FE,
+ CMSG_NETIF_FE_INTERFACE_STATUS,
+ 0,
+ sizeof(fmsg),
+ &fmsg,
+ d);
+ }
+}
+
+static void
+process_netif_fe_message(control_msg_t *m, struct domain *d)
+{
+ switch (m->subtype) {
+ case CMSG_NETIF_FE_DRIVER_STATUS:
+ {
+ netif_fe_driver_status_t *sh =
+ (netif_fe_driver_status_t *)m->msg;
+ handle_netif_fe_driver_status(m, sh, d);
+ break;
+ }
+ case CMSG_NETIF_FE_INTERFACE_CONNECT:
+ {
+ netif_fe_interface_connect_t *ic =
+ (netif_fe_interface_connect_t *)m->msg;
+ handle_netif_fe_interface_connect(m, ic, d);
+ break;
+ }
+ default:
+ warnx("unknown netif front end message subtype %d",
+ m->subtype);
+ }
+}
+
+static void
+process_control_message(control_msg_t *msg, struct domain *d)
+{
+ control_msg_t m;
+
+ /* Don't want a malicous domain messing us about, so copy the
+ control mesasge into a local buffer. */
+ memcpy(&m, msg, sizeof(m));
+ switch (m.type) {
+ case CMSG_CONSOLE:
+ process_console_control_message(&m, d);
+ break;
+ case CMSG_BLKIF_FE:
+ process_blkif_fe_message(&m, d);
+ break;
+ case CMSG_NETIF_FE:
+ process_netif_fe_message(&m, d);
+ break;
+ default:
+ warnx("unknown control message type %d", m.type);
+ }
+}
+
+static void
+domain_did_control_event(struct domain *d)
+{
+ CONTROL_RING_IDX c;
+
+ /* Pick up and process control ring messages. */
+ while (d->tx_req_cons != d->ctrl_if->tx_req_prod) {
+ c = d->tx_req_cons % CONTROL_RING_SIZE;
+ process_control_message(&d->ctrl_if->tx_ring[c], d);
+ d->tx_req_cons++;
+ assert(d->tx_req_cons <= d->ctrl_if->tx_req_prod);
+ PRINTF(5, "req_cons %ld, req_prod %ld.\n",
+ d->tx_req_cons, d->ctrl_if->tx_req_prod);
+ }
+
+ /* Take any replies off, and discard them. */
+ if (d->rx_resp_cons != d->ctrl_if->rx_resp_prod)
+ PRINTF(1, "discard %ld events\n",
+ d->ctrl_if->rx_resp_prod -
+ d->rx_resp_cons);
+ d->rx_resp_cons = d->ctrl_if->rx_resp_prod;
+}
+
+/* This is the main function for domain control threads */
+void *
+domain_thread_func(void *D)
+{
+ struct domain *d = D;
+ int r;
+ CONTROL_RING_IDX old_resp_prod, old_req_prod;
+
+ pthread_mutex_lock(&d->mux);
+ for (;;) {
+ pthread_cond_wait(&d->cond, &d->mux);
+
+ old_resp_prod = d->ctrl_if->tx_resp_prod;
+ old_req_prod = d->ctrl_if->rx_req_prod;
+
+ domain_did_control_event(d);
+ if (d->cc && d->cc->in_buf_used != 0 && d->plugged == 0) {
+ r = d->cc->in_buf_used;
+ if (r > 60)
+ r = 60;
+ PRINTF(1, "Sending to domain: %.*s\n",
+ r, d->cc->in_buf);
+ send_control_message(CMSG_CONSOLE,
+ CMSG_CONSOLE_DATA,
+ 0,
+ r,
+ d->cc->in_buf,
+ d);
+ memmove(d->cc->in_buf, d->cc->in_buf + r,
+ d->cc->in_buf_used - r);
+ d->cc->in_buf_used -= r;
+ }
+
+ if (d->ctrl_if->tx_resp_prod != old_resp_prod ||
+ d->ctrl_if->rx_req_prod != old_req_prod)
+ xc_evtchn_send(xc_handle, d->control_evtchn);
+ }
+}
+
+/* This is the only thing you can do with a domain structure if you're
+ not in the thread which controls that domain. Domain 0 is
+ special. */
+void
+signal_domain(struct domain *d)
+{
+ CONTROL_RING_IDX c;
+ int id;
+ struct event_receiver *evt;
+
+ pthread_mutex_lock(&d->mux);
+ if (d == dom0) {
+ /* Take events off of dom0's control ring, and send
+ them to the event receivers. */
+ while (d->tx_req_cons != d->ctrl_if->tx_req_prod) {
+ c = d->tx_req_cons % CONTROL_RING_SIZE;
+ id = d->ctrl_if->tx_ring[c].id;
+ evt = find_event_receiver(id);
+ if (evt != NULL) {
+ PRINTF(1, "delivering event id %d\n", evt->id);
+ pthread_cond_broadcast(&evt->cond);
+ pthread_mutex_unlock(&d->mux);
+ sched_yield();
+ pthread_mutex_lock(&d->mux);
+ } else {
+ warnx("unexpected message id %d discarded",
+ id);
+ d->tx_req_cons++;
+ }
+ }
+ while (d->rx_resp_cons != d->ctrl_if->rx_resp_prod) {
+ c = d->rx_resp_cons % CONTROL_RING_SIZE;
+ id = d->ctrl_if->rx_ring[c].id;
+ evt = find_event_receiver(id);
+ if (evt != NULL) {
+ PRINTF(1, "delivering event rep id %d\n", evt->id);
+ pthread_cond_broadcast(&evt->cond);
+ pthread_mutex_unlock(&d->mux);
+ sched_yield();
+ pthread_mutex_lock(&d->mux);
+ } else {
+ warnx("unexpected message reply id %d discarded",
+ id);
+ d->rx_resp_cons++;
+ }
+ }
+ } else {
+ if (d->plugged) {
+ d->event_pending = 1;
+ } else {
+ pthread_cond_broadcast(&d->cond);
+ }
+ }
+ pthread_mutex_unlock(&d->mux);
+}
+
+static void
+handle_evtchn_event(void)
+{
+ short port;
+ struct domain *d;
+
+ read(evtchn_fd, &port, sizeof(short));
+ write(evtchn_fd, &port, sizeof(short));
+ foreach_domain (d) {
+ if (d->control_evtchn == port) {
+ signal_domain(d);
+ return;
+ }
+ }
+ warnx("got an event on an unknown port %d", port);
+}
+
+void *
+map_domain_mem(struct domain *d, unsigned long mfn)
+{
+ return xc_map_foreign_range(xc_handle, d->domid,
+ PAGE_SIZE, PROT_READ | PROT_WRITE,
+ mfn);
+}
+
+static void
+handle_console_event(struct console_connection *cc)
+{
+ int r;
+ int fd;
+
+ switch (cc->state) {
+ case CC_STATE_ERROR:
+ /* Errors shouldn't get here. */
+ abort();
+ case CC_STATE_PENDING:
+ fd = accept(cc->fd, NULL, NULL);
+ if (fd >= 0) {
+ PRINTF(3, "Accepted console connection for domain %d",
+ cc->dom->domid);
+ close(cc->fd);
+ cc->fd = fd;
+ cc->state = CC_STATE_CONNECTED;
+ while (cc->buf_used != 0) {
+ r = write(cc->fd,
+ cc->buf,
+ cc->buf_used);
+ if (r <= 0) {
+ cc->state = CC_STATE_ERROR;
+ break;
+ }
+ memmove(cc->buf,
+ cc->buf + r,
+ cc->buf_used - r);
+ cc->buf_used -= r;
+ }
+ free(cc->buf);
+ cc->buf = NULL;
+ cc->buf_allocated = 0;
+ } else {
+ PRINTF(1, "error %s accepting console", strerror(errno));
+ }
+ pthread_mutex_unlock(&cc->dom->mux);
+ break;
+ case CC_STATE_CONNECTED:
+ if (cc->in_buf_allocated == 0) {
+ assert(cc->in_buf_used == 0);
+ cc->in_buf_allocated = 128;
+ cc->in_buf = xmalloc(cc->in_buf_allocated);
+ }
+ if (cc->in_buf_used == cc->in_buf_allocated) {
+ cc->in_buf_allocated *= 2;
+ cc->in_buf = xrealloc(cc->in_buf, cc->in_buf_allocated);
+ }
+ r = read(cc->fd, cc->in_buf + cc->in_buf_used,
+ cc->in_buf_allocated - cc->in_buf_used);
+ if (r <= 0) {
+ cc->state = CC_STATE_ERROR;
+ } else {
+ cc->in_buf_used += r;
+ }
+ pthread_mutex_unlock(&cc->dom->mux);
+ signal_domain(cc->dom);
+ break;
+ }
+}
+
+static void
+handle_connection_event(struct open_connection *oc)
+{
+ int r;
+
+ /* We know that some amount of data is ready and waiting for
+ us. Slurp it in. */
+ if (oc->buf_used == oc->buf_allocated) {
+ oc->buf_allocated *= 2;
+ oc->buf = xrealloc(oc->buf, oc->buf_allocated);
+ }
+ r = read(oc->fd, oc->buf + oc->buf_used,
+ oc->buf_allocated - oc->buf_used);
+ if (r < 0) {
+ warn("reading command from remote");
+ oc->state = OC_STATE_ERROR;
+ } else if (r == 0) {
+ warnx("reading command from remote");
+ oc->state = OC_STATE_ERROR;
+ } else {
+ oc->buf_used += r;
+ if (strchr(oc->buf, '\n'))
+ oc->state = OC_STATE_COMMAND_PENDING;
+ }
+}
+
+static void
+get_and_process_event(void)
+{
+ fd_set read_fds, except_fds;
+ struct open_connection *oc;
+ struct console_connection *cc;
+ int max_fd = listen_fd;
+ int r;
+ struct list_head *li, *temp_li;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&except_fds);
+ FD_SET(listen_fd, &read_fds);
+ FD_SET(evtchn_fd, &read_fds);
+ if (evtchn_fd > max_fd)
+ max_fd = evtchn_fd;
+ foreach_open_connection(oc) {
+ FD_SET(oc->fd, &read_fds);
+ FD_SET(oc->fd, &except_fds);
+ if (oc->fd > max_fd)
+ max_fd = oc->fd;
+ }
+ foreach_console_connection(cc) {
+ FD_SET(cc->fd, &read_fds);
+ FD_SET(cc->fd, &except_fds);
+ if (cc->fd > max_fd)
+ max_fd = cc->fd;
+ }
+
+ r = select(max_fd + 1, &read_fds, NULL, &except_fds, NULL);
+ if (r < 0)
+ err(1, "select");
+ if (FD_ISSET(listen_fd, &read_fds)) {
+ accept_new_connection();
+ } else if (FD_ISSET(evtchn_fd, &read_fds))
+ handle_evtchn_event();
+
+
+ foreach_open_connection(oc) {
+ if (FD_ISSET(oc->fd, &read_fds))
+ handle_connection_event(oc);
+ if (FD_ISSET(oc->fd, &except_fds))
+ oc->state = OC_STATE_ERROR;
+ }
+ list_foreach_safe(&head_console, li, temp_li) {
+ cc = list_item(li, struct console_connection, list);
+ if (FD_ISSET(cc->fd, &read_fds))
+ handle_console_event(cc);
+ if (FD_ISSET(cc->fd, &except_fds) ||
+ cc->state == CC_STATE_ERROR) {
+ PRINTF(1, "Cleaning up console connection");
+ cc->dom->cc = NULL;
+ list_remove(&cc->list);
+ close(cc->fd);
+ if (cc->buf_allocated != 0)
+ free(cc->buf);
+ if (cc->in_buf_allocated != 0)
+ free(cc->in_buf);
+ free(cc);
+ }
+ }
+
+ /* Run pending stuff on the open connections. */
+ list_foreach_safe(&head_connection, li, temp_li) {
+ oc = list_item(li, struct open_connection, connection_list);
+ switch (oc->state) {
+ case OC_STATE_ERROR:
+ list_remove(&oc->connection_list);
+ closedown_connection(oc);
+ break;
+ case OC_STATE_COMMAND_PENDING:
+ process_command(oc);
+ break;
+ case OC_STATE_CONNECTED:
+ /* Don't need to do anything */
+ break;
+ }
+ }
+}
+
+static int
+start_listening(void)
+{
+ int sock;
+ struct sockaddr_in inaddr;
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ err(1, "creating socket");
+ memset(&inaddr, 0, sizeof(inaddr));
+ inaddr.sin_family = AF_INET;
+ inaddr.sin_port = htons(MINIXEND_PORT);
+
+ if (bind(sock, (struct sockaddr *)&inaddr, sizeof(inaddr)) < 0)
+ err(1, "binding to port %d", MINIXEND_PORT);
+ if (listen(sock, 5) < 0)
+ err(1, "listening for connections");
+
+ return sock;
+}
+
+static struct domain *
+find_dom0(void)
+{
+ int r;
+ xc_dominfo_t info;
+ struct domain *work;
+
+ r = xc_domain_getinfo(xc_handle, 0, 1, &info);
+ if (r < 0)
+ err(1, "getting domain 0 information");
+ work = xmalloc(sizeof(*work));
+ work->control_evtchn = 2;
+ if (ioctl(evtchn_fd, EVTCHN_BIND, 2) < 0)
+ err(1, "binding to domain 0 control event channel");
+
+ work->domid = 0;
+ work->name = strdup("dom0");
+ work->mem_kb = info.max_memkb;
+ work->state = DOM_STATE_RUNNING;
+ work->shared_info_mfn = info.shared_info_frame;
+
+ work->shared_info = map_domain_mem(work, info.shared_info_frame);
+ work->ctrl_if = (control_if_t *)((unsigned)work->shared_info + 2048);
+ work->tx_req_cons = work->ctrl_if->tx_req_prod;
+ work->rx_resp_cons = work->ctrl_if->rx_resp_prod;
+
+ pthread_mutex_init(&work->mux, NULL);
+ pthread_cond_init(&work->cond, NULL);
+
+ list_insert_after(&work->domain_list, &head_domain);
+
+ return work;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int r;
+
+ r = our_system(NETWORK_SCRIPT " start antispoof=no");
+ if (r < 0)
+ err(1, "running " NETWORK_SCRIPT);
+ if (!WIFEXITED(r)) {
+ if (WIFSIGNALED(r)) {
+ errx(1, NETWORK_SCRIPT " killed by signal %d",
+ WTERMSIG(r));
+ }
+ errx(1, NETWORK_SCRIPT " terminated abnormally");
+ }
+ if (WEXITSTATUS(r) != 0)
+ errx(1, NETWORK_SCRIPT " returned error status %d",
+ WEXITSTATUS(r));
+
+ xc_handle = xc_interface_open();
+
+ listen_fd = start_listening();
+
+ evtchn_fd = open("/dev/xen/evtchn", O_RDWR);
+ if (evtchn_fd < 0)
+ err(1, "openning /dev/xen/evtchn");
+
+ dom0 = find_dom0();
+
+ while (1) {
+ get_and_process_event();
+
+ PRINTF(5, "Dom0 ring state:\n");
+ PRINTF(5, "RX: req_prod %ld, resp_prod %ld, resp_cons %ld\n",
+ dom0->ctrl_if->rx_req_prod,
+ dom0->ctrl_if->rx_resp_prod,
+ dom0->rx_resp_cons);
+ PRINTF(5, "TX: req_prod %ld, resp_prod %ld, req_cons %ld\n",
+ dom0->ctrl_if->tx_req_prod,
+ dom0->ctrl_if->tx_resp_prod,
+ dom0->tx_req_cons);
+ }
+
+ return 0;
+}
+
diff --git a/tools/x2d2/minixend.h b/tools/x2d2/minixend.h
new file mode 100644
index 0000000000..db28d48529
--- /dev/null
+++ b/tools/x2d2/minixend.h
@@ -0,0 +1,154 @@
+#ifndef MINIXEND_H__
+#define MINIXEND_H__
+
+#include <sys/types.h>
+#include <xc.h>
+
+struct list_head {
+ struct list_head *next, **pprev;
+};
+
+struct open_connection {
+ struct list_head connection_list;
+ int fd;
+ enum {
+ OC_STATE_CONNECTED,
+ OC_STATE_ERROR,
+ OC_STATE_COMMAND_PENDING
+ } state;
+
+ /* Buffer of stuff coming from the remote until we get a whole
+ command */
+ int buf_used;
+ int buf_allocated;
+ char *buf;
+};
+
+struct console_connection;
+
+/* Only ever accessed from the domain's controlling thread, unless
+ it's dom0, in which case we perform a moderately complex dance to
+ avoid needing any sort of locking at all. */
+struct domain {
+ struct list_head domain_list;
+ int control_evtchn; /* the local port for the doain control
+ interface event channel. */
+ int domid;
+ char *name;
+ int mem_kb;
+ enum {
+ DOM_STATE_CREATED, /* created but not built */
+ DOM_STATE_PAUSED, /* built but not started or paused */
+ DOM_STATE_RUNNING, /* running normally */
+ DOM_STATE_DEAD /* dead; either destroyed, crashed,
+ or exitted. */
+ } state;
+
+ unsigned long shared_info_mfn;
+ shared_info_t *shared_info;
+ control_if_t *ctrl_if;
+ CONTROL_RING_IDX tx_req_cons;
+ CONTROL_RING_IDX rx_resp_cons;
+
+ unsigned created_netif_backend:1;
+ unsigned plugged:1;
+ unsigned event_pending:1; /* True if an event arrived while
+ the domain was plugged. */
+
+ struct console_connection *cc;
+
+ char netif_mac[6];
+
+ /* Used for two purposes: waking up domain threads when
+ necessary, and synchronising access to dom0, which doesn't
+ have a domain thread. */
+ pthread_mutex_t mux;
+ pthread_cond_t cond;
+
+ pthread_t thread;
+};
+
+struct console_connection {
+ struct list_head list;
+ int fd;
+ struct domain *dom;
+
+ enum {
+ CC_STATE_PENDING,
+ CC_STATE_CONNECTED,
+ CC_STATE_ERROR
+ } state;
+
+ unsigned buf_allocated;
+ unsigned buf_used;
+ char *buf;
+
+ unsigned in_buf_allocated;
+ unsigned in_buf_used;
+ char *in_buf;
+};
+
+
+void *domain_thread_func(void *d);
+void process_command(struct open_connection *oc);
+
+void *xmalloc(size_t s);
+void *xrealloc(void *x, size_t s);
+char *xstrdup(const char *s);
+
+int allocate_event_channel(struct domain *d, int event_ports[2]);
+void *map_domain_mem(struct domain *d, unsigned long mfn);
+void signal_domain(struct domain *d);
+int our_system(const char *fmt, ...);
+
+extern unsigned xc_handle;
+#define EVTCHN_BIND _IO('E', 2)
+extern int evtchn_fd;
+
+#define list_item(head, type, field) \
+((type *)((unsigned)(head) - offsetof(type, field)))
+
+#define foreach_item(iter, head, type, field) \
+for ((iter) = list_item((head)->next, type, field); \
+ (iter) != list_item((head), type, field); \
+ (iter) = list_item((iter)->field.next, type, field))
+
+#define list_insert_after(what, head) \
+do { \
+ (what)->next = (head)->next; \
+ (what)->pprev = &(head)->next; \
+ (head)->next->pprev = &(what)->next; \
+ (head)->next = what; \
+} while (0)
+
+#define list_remove(head) \
+(head)->next->pprev = (head)->pprev; \
+*(head)->pprev = (head)->next;
+
+#define list_foreach_safe(head, li, temp) \
+for ((li) = (head)->next, (temp) = (li)->next; \
+ (li) != (head); \
+ (li) = (temp), (temp) = (li)->next)
+
+#define LIST_HEAD(x) { (x), &(x)->next }
+
+
+extern struct list_head head_domain;
+extern struct list_head head_console;
+
+#define foreach_domain(d) \
+foreach_item(d, &head_domain, struct domain, domain_list)
+#define foreach_console_connection(cc) \
+foreach_item(cc, &head_console, struct console_connection, list)
+
+
+#define CURRENT_LOG_LEVEL 0
+
+#define PRINTF(level, ...) \
+do { \
+ if ((level) >= CURRENT_LOG_LEVEL) \
+ printf(__VA_ARGS__); \
+} while (0)
+
+
+#endif /* MINIXEND_H__ */
diff --git a/tools/x2d2/util.c b/tools/x2d2/util.c
new file mode 100644
index 0000000000..9994c7dfb8
--- /dev/null
+++ b/tools/x2d2/util.c
@@ -0,0 +1,132 @@
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+void *
+xmalloc(size_t s)
+{
+ void *x;
+
+ x = malloc(s);
+ if (x == NULL)
+ err(1, "allocating memory");
+ memset(x, 0, s);
+ return x;
+}
+
+void *
+xrealloc(void *x, size_t s)
+{
+ void *y;
+ y = realloc(x, s);
+ if (y == NULL)
+ err(1, "allocating more memory");
+ return y;
+}
+
+char *
+xstrdup(const char *s)
+{
+ char *x = strdup(s);
+ if (x == NULL)
+ err(1, "duplicating %s", s);
+ return x;
+}
+
+/* Slightly less stupid implementation of system(). We return
+ negative iff there is an error executing the shell; otherwise, we
+ return the wait status as reported by waitpid(). Also, we support
+ printf-style escapes. We don't handle setting the SIGCHLD handler
+ to SIGIGN, though: in that case, we have a race. */
+int
+our_system(const char *fmt, ...)
+{
+ char *cmd = NULL;
+ int r;
+ va_list ap;
+ pid_t child = -1;
+ int pip[2] = {-1, -1};
+ int e;
+ fd_set fds;
+ struct timeval to;
+ int res;
+ pid_t c;
+ unsigned status;
+
+ va_start(ap, fmt);
+ r = vasprintf(&cmd, fmt, ap);
+ va_end(ap);
+ if (r < 0)
+ return r;
+ r = pipe(pip);
+ if (r < 0) {
+ res = r;
+ goto out;
+ }
+ child = fork();
+ if (child < 0) {
+ res = child;
+ goto out;
+ }
+ if (child == 0) {
+ close(pip[0]);
+ fcntl(pip[1], F_SETFD, 1);
+ r = execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
+ /* Uh oh, exec failed */
+ write(pip[1], &r, sizeof(r));
+ _exit(1);
+ }
+
+ close(pip[1]);
+ pip[1] = -1;
+
+ c = waitpid(child, &status, 0);
+ if (c < 0) {
+ res = c;
+ goto out;
+ }
+ assert(c == child);
+ child = -1;
+
+ /* Check execl result */
+ FD_ZERO(&fds);
+ FD_SET(pip[0], &fds);
+ memset(&to, 0, sizeof(to));
+ r = select(pip[0]+1, &fds, NULL, NULL, &to);
+ if (r == 0) {
+ res = status;
+ } else {
+ assert(FD_ISSET(pip[0], &fds));
+ r = read(pip[0], &res, sizeof(res));
+ if (r != sizeof(res))
+ res = status;
+ }
+ close(pip[0]);
+ pip[0] = -1;
+
+ out:
+ e = errno;
+ if (child >= 0) {
+ /* Not obvious what the correct thing to do here is. */
+ /* Don't want to kill the child; that will create a
+ zombie. */
+// kill(child, 9);
+ }
+ if (pip[0] >= 0)
+ close(pip[0]);
+ if (pip[1] >= 0)
+ close(pip[1]);
+ free(cmd);
+ errno = e;
+ return res;
+}
diff --git a/tools/xcs/Makefile b/tools/xcs/Makefile
new file mode 100644
index 0000000000..b24f81101b
--- /dev/null
+++ b/tools/xcs/Makefile
@@ -0,0 +1,48 @@
+# Makefile for XCS
+# Andrew Warfield, 2004
+
+XEN_ROOT=../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+XCS_INSTALL_DIR = /usr/sbin
+
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
+
+CC = gcc
+CFLAGS = -Wall -Werror -g3 -D _XOPEN_SOURCE=600
+
+CFLAGS += -I $(XEN_XC)
+CFLAGS += -I $(XEN_LIBXC)
+CFLAGS += -I $(XEN_LIBXUTIL)
+
+SRCS :=
+SRCS += ctrl_interface.c
+SRCS += bindings.c
+SRCS += connection.c
+SRCS += evtchn.c
+SRCS += xcs.c
+
+HDRS = $(wildcard *.h)
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+BIN = xcs
+
+all: $(BIN) xcsdump
+
+clean:
+ $(RM) *.a *.so *.o *.rpm $(BIN) ctrl_dump
+
+xcsdump: xcsdump.c
+ $(CC) $(CFLAGS) -o xcsdump xcsdump.c -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) \
+ ctrl_interface.c evtchn.c -lxc -lxutil
+
+$(BIN): $(OBJS)
+ $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -lxc -lxutil
+
+install: xcs xcsdump
+ $(INSTALL_DIR) -p $(DESTDIR)/$(XCS_INSTALL_DIR)
+ $(INSTALL_DIR) -p $(DESTDIR)/usr/include
+ $(INSTALL_PROG) xcs $(DESTDIR)/$(XCS_INSTALL_DIR)
+ $(INSTALL_PROG) xcsdump $(DESTDIR)/$(XCS_INSTALL_DIR)
+ $(INSTALL_PROG) xcs_proto.h $(DESTDIR)/usr/include
diff --git a/tools/xcs/bindings.c b/tools/xcs/bindings.c
new file mode 100644
index 0000000000..9b09f51568
--- /dev/null
+++ b/tools/xcs/bindings.c
@@ -0,0 +1,179 @@
+/* bindings.c
+ *
+ * Manage subscriptions for the control interface switch.
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+/* Interfaces:
+ *
+ * xcs_bind (port, type, connection)
+ * - Register connection to receive messages of this type.
+ * xcs_unbind (port, type, connection)
+ * - Remove an existing registration. (Must be an exact match)
+ * xcs_lookup (port, type)
+ * - Return a list of connections matching a registration.
+ *
+ * - All connections have a connection.bindings list of current bindings.
+ * - (port, type) pairs may be wildcarded with -1.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "xcs.h"
+
+
+typedef struct binding_ent_st {
+ connection_t *con;
+ struct binding_ent_st *next;
+} binding_ent_t;
+
+#define BINDING_TABLE_SIZE 1024
+
+static binding_ent_t *binding_table[BINDING_TABLE_SIZE];
+
+#define PORT_WILD(_ent) ((_ent)->port == PORT_WILDCARD)
+#define TYPE_WILD(_ent) ((_ent)->type == TYPE_WILDCARD)
+#define FULLY_WILD(_ent) (PORT_WILD(_ent) && TYPE_WILD(_ent))
+
+#define BINDING_HASH(_key) \
+ ((((_key)->port * 11) ^ (_key)->type) % BINDING_TABLE_SIZE)
+
+
+void init_bindings(void)
+{
+ memset(binding_table, 0, sizeof(binding_table));
+}
+
+static int table_add(binding_ent_t *table[],
+ connection_t *con,
+ binding_key_t *key)
+{
+ binding_ent_t **curs, *ent;
+
+ curs = &table[BINDING_HASH(key)];
+
+ while (*curs != NULL) {
+ if ((*curs)->con == con) {
+ DPRINTF("Tried to add an ent that already existed.\n");
+ goto done;
+ }
+ curs = &(*curs)->next;
+ }
+
+ if (connection_add_binding(con, key) != 0)
+ {
+ DPRINTF("couldn't add binding on connection (%lu)\n", con->id);
+ goto fail;
+ }
+ ent = (binding_ent_t *)malloc(sizeof(binding_ent_t));
+ if (ent == 0) {
+ DPRINTF("couldn't alloc binding ent!\n");
+ goto fail;
+ }
+ ent->con = con;
+ ent->next = NULL;
+ *curs = ent;
+
+done:
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+static inline int binding_has_colliding_hashes(connection_t *con,
+ binding_key_t *key)
+{
+ int hash, count = 0;
+ binding_key_ent_t *ent;
+
+ ent = con->bindings;
+ hash = BINDING_HASH(key);
+
+ while (ent != NULL) {
+ if (BINDING_HASH(&ent->key) == hash) count ++;
+ ent = ent->next;
+ }
+
+ return (count > 1);
+}
+static int table_remove(binding_ent_t *table[],
+ connection_t *con,
+ binding_key_t *key)
+{
+ binding_ent_t **curs, *ent;
+
+ if (!binding_has_colliding_hashes(con, key))
+ {
+
+ curs = &table[BINDING_HASH(key)];
+
+ while ((*curs != NULL) && ((*curs)->con != con))
+ curs = &(*curs)->next;
+
+ if (*curs != NULL) {
+ ent = *curs;
+ *curs = (*curs)->next;
+ free(ent);
+ }
+ }
+
+ connection_remove_binding(con, key);
+
+ return 0;
+}
+
+int xcs_bind(connection_t *con, int port, u16 type)
+{
+ binding_key_t key;
+
+ key.port = port;
+ key.type = type;
+
+ return table_add(binding_table, con, &key);
+}
+
+int xcs_unbind(connection_t *con, int port, u16 type)
+{
+ binding_key_t key;
+
+ key.port = port;
+ key.type = type;
+
+ return table_remove(binding_table, con, &key);
+}
+
+
+static void for_each_binding(binding_ent_t *list, binding_key_t *key,
+ void (*f)(connection_t *, void *), void *arg)
+{
+ while (list != NULL)
+ {
+ if (connection_has_binding(list->con, key))
+ f(list->con, arg);
+ list = list->next;
+ }
+}
+
+void xcs_lookup(int port, u16 type, void (*f)(connection_t *, void *),
+ void *arg)
+{
+ binding_key_t key;
+
+ key.port = port; key.type = type;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = port; key.type = TYPE_WILDCARD;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = PORT_WILDCARD; key.type = type;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = PORT_WILDCARD; key.type = TYPE_WILDCARD;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+}
diff --git a/tools/xcs/connection.c b/tools/xcs/connection.c
new file mode 100644
index 0000000000..3b5747de68
--- /dev/null
+++ b/tools/xcs/connection.c
@@ -0,0 +1,157 @@
+/*
+ * connection.c
+ *
+ * State associated with a client connection to xcs.
+ *
+ * Copyright (c) 2004, Andrew Warfield
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xcs.h"
+
+connection_t *connection_list = NULL;
+
+#define CONNECTED(_c) (((_c)->ctrl_fd != -1) || ((_c)->data_fd != -1))
+
+connection_t *get_con_by_session(unsigned long session_id)
+{
+ connection_t **c, *ent = NULL;
+
+ c = &connection_list;
+
+ DPRINTF("looking for id: %lu : %lu\n", session_id, (*c)->id);
+
+ while (*c != NULL)
+ {
+ if ((*c)->id == session_id)
+ return (*c);
+ c = &(*c)->next;
+ }
+
+ return ent;
+}
+
+connection_t *connection_new()
+{
+ connection_t *con;
+
+ con = (connection_t *)malloc(sizeof(connection_t));
+ if (con == NULL)
+ {
+ DPRINTF("couldn't allocate a new connection\n");
+ return NULL;
+ }
+
+ con->bindings = NULL;
+ con->data_fd = -1;
+ con->ctrl_fd = -1;
+
+ /* connections need a unique session id.
+ * - this approach probably gets fixed later, but for the moment
+ * is unique, and clearly identifies a connection.
+ */
+ con->id = (unsigned long)con;
+
+ /* add it to the connection list */
+ con->next = connection_list;
+ connection_list = con;
+
+ return (con);
+}
+
+void connection_free(connection_t *con)
+{
+ /* first free all subscribed bindings: */
+
+ while (con->bindings != NULL)
+ xcs_unbind(con, con->bindings->key.port, con->bindings->key.type);
+
+ /* now free the connection. */
+ free(con);
+}
+
+int connection_add_binding(connection_t *con, binding_key_t *key)
+{
+ binding_key_ent_t *key_ent;
+
+ key_ent = (binding_key_ent_t *)malloc(sizeof(binding_key_ent_t));
+ if (key_ent == NULL)
+ {
+ DPRINTF("couldn't alloc key in connection_add_binding\n");
+ return -1;
+ }
+
+ key_ent->key = *key;
+ key_ent->next = con->bindings;
+ con->bindings = key_ent;
+
+ return 0;
+}
+
+int connection_remove_binding(connection_t *con, binding_key_t *key)
+{
+ binding_key_ent_t *key_ent;
+ binding_key_ent_t **curs = &con->bindings;
+
+ while ((*curs != NULL) && (!BINDING_KEYS_EQUAL(&(*curs)->key, key)))
+ curs = &(*curs)->next;
+
+ if (*curs != NULL) {
+ key_ent = *curs;
+ *curs = (*curs)->next;
+ free(key_ent);
+ }
+
+ return 0;
+}
+
+
+int connection_has_binding(connection_t *con, binding_key_t *key)
+{
+ binding_key_ent_t *ent;
+ int ret = 0;
+
+ ent = con->bindings;
+
+ while (ent != NULL)
+ {
+ if (BINDING_KEYS_EQUAL(key, &ent->key))
+ {
+ ret = 1;
+ break;
+ }
+ ent = ent->next;
+ }
+
+ return ret;
+}
+
+
+void gc_connection_list(void)
+{
+ connection_t **c, *ent = NULL;
+ struct timeval now, delta;
+
+ c = &connection_list;
+ gettimeofday(&now, NULL);
+
+ while ( *c != NULL )
+ {
+ if ( !CONNECTED(*c) )
+ {
+ timersub(&now, &(*c)->disconnect_time, &delta);
+ if ( delta.tv_sec >= XCS_SESSION_TIMEOUT )
+ {
+ DPRINTF(" : Freeing connection %lu after %lds\n",
+ (*c)->id, delta.tv_sec);
+ ent = *c;
+ *c = (*c)->next;
+ connection_free(ent);
+ continue;
+ }
+ }
+ c = &(*c)->next;
+ }
+}
diff --git a/tools/xcs/ctrl_interface.c b/tools/xcs/ctrl_interface.c
new file mode 100644
index 0000000000..0896910cb4
--- /dev/null
+++ b/tools/xcs/ctrl_interface.c
@@ -0,0 +1,269 @@
+/* control_interface.c
+ *
+ * Interfaces to control message rings to VMs.
+ *
+ * Most of this is directly based on the original xu interface to python
+ * written by Keir Fraser.
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "xcs.h"
+
+static int xc_handle = -1;
+
+/* Called at start-of-day when using the control channel interface. */
+int ctrl_chan_init(void)
+{
+ if ( (xc_handle = xc_interface_open()) == -1 )
+ {
+ DPRINTF("Could not open Xen control interface");
+ return -1;
+ }
+
+ return 0;
+}
+
+static control_if_t *map_control_interface(int fd, unsigned long pfn,
+ u32 dom)
+{
+ char *vaddr = xc_map_foreign_range( fd, dom, PAGE_SIZE,
+ PROT_READ|PROT_WRITE, pfn );
+ if ( vaddr == NULL )
+ return NULL;
+ return (control_if_t *)(vaddr + 2048);
+}
+
+static void unmap_control_interface(int fd, control_if_t *c)
+{
+ char *vaddr = (char *)c - 2048;
+ (void)munmap(vaddr, PAGE_SIZE);
+}
+
+int ctrl_chan_notify(control_channel_t *cc)
+{
+ return xc_evtchn_send(xc_handle, cc->local_port);
+}
+
+int ctrl_chan_read_request(control_channel_t *cc, xcs_control_msg_t *dmsg)
+{
+ control_msg_t *smsg;
+ RING_IDX c = cc->tx_ring.req_cons;
+
+ if ( !RING_HAS_UNCONSUMED_REQUESTS(CTRL_RING, &cc->tx_ring) )
+ {
+ DPRINTF("no request to read\n");
+ return -1;
+ }
+
+ rmb(); /* make sure we see the data associated with the request */
+ smsg = RING_GET_REQUEST(CTRL_RING, &cc->tx_ring, c);
+ memcpy(&dmsg->msg, smsg, sizeof(*smsg));
+ if ( dmsg->msg.length > sizeof(dmsg->msg.msg) )
+ dmsg->msg.length = sizeof(dmsg->msg.msg);
+ cc->tx_ring.req_cons++;
+ return 0;
+}
+
+int ctrl_chan_write_request(control_channel_t *cc,
+ xcs_control_msg_t *smsg)
+{
+ control_msg_t *dmsg;
+ RING_IDX p = cc->rx_ring.req_prod_pvt;
+
+ if ( RING_FULL(CTRL_RING, &cc->rx_ring) )
+ {
+ DPRINTF("no space to write request");
+ return -ENOSPC;
+ }
+
+ dmsg = RING_GET_REQUEST(CTRL_RING, &cc->rx_ring, p);
+ memcpy(dmsg, &smsg->msg, sizeof(*dmsg));
+
+ wmb();
+ cc->rx_ring.req_prod_pvt++;
+ RING_PUSH_REQUESTS(CTRL_RING, &cc->rx_ring);
+
+ return 0;
+}
+
+int ctrl_chan_read_response(control_channel_t *cc, xcs_control_msg_t *dmsg)
+{
+ control_msg_t *smsg;
+ RING_IDX c = cc->rx_ring.rsp_cons;
+
+ if ( !RING_HAS_UNCONSUMED_RESPONSES(CTRL_RING, &cc->rx_ring) )
+ {
+ DPRINTF("no response to read");
+ return -1;
+ }
+
+ rmb(); /* make sure we see the data associated with the request */
+ smsg = RING_GET_RESPONSE(CTRL_RING, &cc->rx_ring, c);
+ memcpy(&dmsg->msg, smsg, sizeof(*smsg));
+ if ( dmsg->msg.length > sizeof(dmsg->msg.msg) )
+ dmsg->msg.length = sizeof(dmsg->msg.msg);
+ cc->rx_ring.rsp_cons++;
+ return 0;
+}
+
+int ctrl_chan_write_response(control_channel_t *cc,
+ xcs_control_msg_t *smsg)
+{
+ control_msg_t *dmsg;
+ RING_IDX p = cc->tx_ring.rsp_prod_pvt;
+
+ /* akw: if the ring is synchronous, you should never need this test! */
+ /* (but it was in the original code... ) */
+ if ( cc->tx_ring.req_cons == cc->tx_ring.rsp_prod_pvt )
+ {
+ DPRINTF("no space to write response");
+ return -ENOSPC;
+ }
+
+ dmsg = RING_GET_RESPONSE(CTRL_RING, &cc->tx_ring, p);
+ memcpy(dmsg, &smsg->msg, sizeof(*dmsg));
+
+ wmb();
+ cc->tx_ring.rsp_prod_pvt++;
+ RING_PUSH_RESPONSES(CTRL_RING, &cc->tx_ring);
+
+ return 0;
+}
+
+int ctrl_chan_request_to_read(control_channel_t *cc)
+{
+ return (RING_HAS_UNCONSUMED_REQUESTS(CTRL_RING, &cc->tx_ring));
+}
+
+int ctrl_chan_space_to_write_request(control_channel_t *cc)
+{
+ return (!(RING_FULL(CTRL_RING, &cc->rx_ring)));
+}
+
+int ctrl_chan_response_to_read(control_channel_t *cc)
+{
+ return (RING_HAS_UNCONSUMED_RESPONSES(CTRL_RING, &cc->rx_ring));
+}
+
+int ctrl_chan_space_to_write_response(control_channel_t *cc)
+{
+ /* again, there is something fishy here. */
+ return ( cc->tx_ring.req_cons != cc->tx_ring.rsp_prod_pvt );
+}
+
+int ctrl_chan_connect(control_channel_t *cc)
+{
+ xc_dominfo_t info;
+
+ if ( cc->connected )
+ {
+ return 0;
+ }
+
+ if ( (xc_domain_getinfo(xc_handle, cc->remote_dom, 1, &info) != 1) ||
+ (info.domid != cc->remote_dom) )
+ {
+ DPRINTF("Failed to obtain domain status");
+ return -1;
+ }
+
+ cc->interface =
+ map_control_interface(xc_handle, info.shared_info_frame,
+ cc->remote_dom);
+
+ if ( cc->interface == NULL )
+ {
+ DPRINTF("Failed to map domain control interface");
+ return -1;
+ }
+
+ /* Synchronise ring indexes. */
+ BACK_RING_ATTACH(CTRL_RING, &cc->tx_ring, &cc->interface->tx_ring);
+ FRONT_RING_ATTACH(CTRL_RING, &cc->rx_ring, &cc->interface->rx_ring);
+
+ cc->connected = 1;
+
+ return 0;
+}
+
+void ctrl_chan_disconnect(control_channel_t *cc)
+{
+ if ( cc->connected )
+ unmap_control_interface(xc_handle, cc->interface);
+ cc->connected = 0;
+}
+
+
+control_channel_t *ctrl_chan_new(u32 dom, int local_port, int remote_port)
+{
+ control_channel_t *cc;
+
+ cc = (control_channel_t *)malloc(sizeof(control_channel_t));
+ if ( cc == NULL ) return NULL;
+
+ cc->connected = 0;
+ cc->remote_dom = dom;
+
+ if ( dom == 0 )
+ {
+ /*
+ * The control-interface event channel for DOM0 is already set up.
+ * We use an ioctl to discover the port at our end of the channel.
+ */
+ local_port = ioctl(xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN,
+ NULL);
+ remote_port = -1; /* We don't need the remote end of the DOM0 link. */
+ if ( local_port < 0 )
+ {
+ DPRINTF("Could not open channel to DOM0");
+ goto fail;
+ }
+ }
+ else if ( xc_evtchn_bind_interdomain(xc_handle,
+ DOMID_SELF, dom,
+ &local_port, &remote_port) != 0 )
+ {
+ DPRINTF("Could not open channel to domain");
+ goto fail;
+ }
+
+ cc->local_port = local_port;
+ cc->remote_port = remote_port;
+
+ if ( ctrl_chan_connect(cc) != 0 )
+ goto fail;
+
+ return cc;
+
+ fail:
+ if ( dom != 0 )
+ (void)xc_evtchn_close(xc_handle, DOMID_SELF, local_port);
+
+ free(cc);
+
+ return NULL;
+}
+
+void ctrl_chan_free(control_channel_t *cc)
+{
+ ctrl_chan_disconnect(cc);
+ if ( cc->remote_dom != 0 )
+ (void)xc_evtchn_close(xc_handle, DOMID_SELF, cc->local_port);
+ free(cc);
+}
+
+
+/* other libxc commands: */
+
+int ctrl_chan_bind_virq(int virq, int *port)
+{
+ return xc_evtchn_bind_virq(xc_handle, virq, port);
+}
diff --git a/tools/xcs/evtchn.c b/tools/xcs/evtchn.c
new file mode 100644
index 0000000000..a96036db37
--- /dev/null
+++ b/tools/xcs/evtchn.c
@@ -0,0 +1,108 @@
+/* evtchn.c
+ *
+ * Interfaces to event channel driver.
+ *
+ * Most of this is directly based on the original xu interface to python
+ * written by Keir Fraser.
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h> /* XOPEN drops makedev, this gets it back. */
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include "xcs.h"
+
+static int evtchn_fd = -1;
+
+/* NB. The following should be kept in sync with the kernel's evtchn driver. */
+#define EVTCHN_DEV_NAME "/dev/xen/evtchn"
+#define EVTCHN_DEV_MAJOR 10
+#define EVTCHN_DEV_MINOR 201
+/* /dev/xen/evtchn ioctls: */
+/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
+#define EVTCHN_RESET _IO('E', 1)
+/* EVTCHN_BIND: Bind to teh specified event-channel port. */
+#define EVTCHN_BIND _IO('E', 2)
+/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */
+#define EVTCHN_UNBIND _IO('E', 3)
+
+int evtchn_read()
+{
+ u16 v;
+ int bytes;
+
+ while ( (bytes = read(evtchn_fd, &v, sizeof(v))) == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ /* EAGAIN was cased to return 'None' in the python version... */
+ return -errno;
+ }
+
+ if ( bytes == sizeof(v) )
+ return v;
+
+ /* bad return */
+ return -1;
+}
+
+void evtchn_unmask(u16 idx)
+{
+ (void)write(evtchn_fd, &idx, sizeof(idx));
+}
+
+int evtchn_bind(int idx)
+{
+ if ( ioctl(evtchn_fd, EVTCHN_BIND, idx) != 0 )
+ return -errno;
+
+ return 0;
+}
+
+int evtchn_unbind(int idx)
+{
+ if ( ioctl(evtchn_fd, EVTCHN_UNBIND, idx) != 0 )
+ return -errno;
+
+ return 0;
+}
+
+int evtchn_open(void)
+{
+ struct stat st;
+
+ /* Make sure any existing device file links to correct device. */
+ if ( (lstat(EVTCHN_DEV_NAME, &st) != 0) ||
+ !S_ISCHR(st.st_mode) ||
+ (st.st_rdev != makedev(EVTCHN_DEV_MAJOR, EVTCHN_DEV_MINOR)) )
+ (void)unlink(EVTCHN_DEV_NAME);
+
+ reopen:
+ evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
+ if ( evtchn_fd == -1 )
+ {
+ if ( (errno == ENOENT) &&
+ ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) &&
+ (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600,
+ makedev(EVTCHN_DEV_MAJOR,EVTCHN_DEV_MINOR)) == 0) )
+ goto reopen;
+ return -errno;
+ }
+ /*set_cloexec(evtchn_fd); -- no longer required*/
+printf("Eventchan_fd is %d\n", evtchn_fd);
+ return evtchn_fd;
+}
+
+void evtchn_close()
+{
+ (void)close(evtchn_fd);
+ evtchn_fd = -1;
+}
+
diff --git a/tools/xcs/xcs.c b/tools/xcs/xcs.c
new file mode 100644
index 0000000000..f0b1a96a88
--- /dev/null
+++ b/tools/xcs/xcs.c
@@ -0,0 +1,880 @@
+/* xcs.c
+ *
+ * xcs - Xen Control Switch
+ *
+ * Copyright (c) 2004, Andrew Warfield
+ */
+
+/*
+
+ Things we need to select on in xcs:
+
+ 1. Events arriving on /dev/evtchn
+
+ These will kick a function to read everything off the fd, and scan the
+ associated control message rings, resulting in notifications sent on
+ data channels to connected clients.
+
+ 2. New TCP connections on XCS_PORT.
+
+ These will either be control (intially) or associated data connections.
+
+ Control connections will instantiate or rebind to an existing connnection
+ struct. The control channel is used to configure what events will be
+ received on an associated data channel. These two channels are split
+ out because the control channel is synchronous, all messages will return
+ a result from XCS. The data channel is effectively asynchronous, events
+ may arrive in the middle of a control message exchange. Additionally,
+ Having two TCP connections allows the client side to have a blocking
+ listen loop for data messages, while independently interacting on the
+ control channel at other places in the code.
+
+ Data connections attach to an existing control struct, using a session
+ id that is passed during the control connect. There is currently a
+ one-to-one relationship between data and control channels, but there
+ could just as easily be many data channels, if there were a set of
+ clients with identical interests, or if you wanted to trace an existing
+ client's data traffic.
+
+ 3. Messages arriving on open TCP connections.
+ There are three types of open connections:
+
+ 3a. Messages arriving on open control channel file descriptors.
+
+ [description of the control protocol here]
+
+ 3b. Messages arriving on open data channel file descriptors.
+
+ [description of the data protocol here]
+
+ 3c. Messages arriving on (new) unbound connections.
+
+ A connection must issue a XCS_CONNECT message to specify what
+ it is, after which the connection is moved into one of the above
+ two groups.
+
+ Additionally, we need a periodic timer to do housekeeping.
+
+ 4. Every XCS_GC_INTERVAL seconds, we need to clean up outstanding state.
+ Specifically, we garbage collect any sessions (connection_t structs)
+ that have been unconnected for a period of time (XCS_SESSION_TIMEOUT),
+ and close any connections that have been openned, but not connected
+ as a control or data connection (XCS_UFD_TIMEOUT).
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <malloc.h>
+#include <fcntl.h>
+#include "xcs.h"
+
+#undef fd_max
+#define fd_max(x,y) ((x) > (y) ? (x) : (y))
+
+/* ------[ Control channel interfaces ]------------------------------------*/
+
+static control_channel_t *cc_list[NR_EVENT_CHANNELS];
+static int *dom_port_map = 0;
+static int dom_port_map_size = 0;
+
+static void map_dom_to_port(u32 dom, int port)
+{
+ if (dom >= dom_port_map_size) {
+ dom_port_map = (int *)realloc(dom_port_map,
+ (dom + 10) * sizeof(dom_port_map[0]));
+
+ if (dom_port_map == NULL) {
+ perror("realloc(dom_port_map)");
+ exit(1);
+ }
+
+ for (; dom_port_map_size < dom + 10; dom_port_map_size++) {
+ dom_port_map[dom_port_map_size] = -1;
+ }
+ }
+
+ dom_port_map[dom] = port;
+}
+
+static int dom_to_port(u32 dom) {
+ if (dom >= dom_port_map_size) return -1;
+
+ return dom_port_map[dom];
+}
+
+static void init_interfaces(void)
+{
+ memset(cc_list, 0, sizeof cc_list);
+}
+
+static control_channel_t *add_interface(u32 dom, int local_port,
+ int remote_port)
+{
+ control_channel_t *cc=NULL, *oldcc;
+ int ret;
+
+ if (cc_list[dom_to_port(dom)] != NULL)
+ {
+ return(cc_list[dom_to_port(dom)]);
+ }
+
+ if (cc_list[local_port] == NULL)
+ {
+ cc = ctrl_chan_new(dom, local_port, remote_port);
+ }
+
+ if (cc == NULL)
+ return NULL;
+
+ DPRINTF("added a new interface: dom: %u (l:%d,r:%d): %p\n",
+ dom, local_port, remote_port, cc);
+ DPRINTF("added a new interface: dom: %u (l:%d,r:%d): %p\n",
+ dom, cc->local_port, cc->remote_port, cc);
+
+ if ((ret = evtchn_bind(cc->local_port)) != 0)
+ {
+ DPRINTF("Got control interface, but couldn't bind evtchan!(%d)\n", ret);
+ ctrl_chan_free(cc);
+ return NULL;
+ }
+
+ if ( cc_list[cc->local_port] != NULL )
+ {
+ oldcc = cc_list[cc->local_port];
+
+ if ((oldcc->remote_dom != cc->remote_dom) ||
+ (oldcc->remote_port != cc->remote_port))
+ {
+ DPRINTF("CC conflict! (port: %d, old dom: %u, new dom: %u)\n",
+ cc->local_port, oldcc->remote_dom, cc->remote_dom);
+ map_dom_to_port(oldcc->remote_dom, -1);
+ ctrl_chan_free(cc_list[cc->local_port]);
+ }
+ }
+
+ cc_list[cc->local_port] = cc;
+ map_dom_to_port(cc->remote_dom, cc->local_port);
+ cc->type = CC_TYPE_INTERDOMAIN;
+ cc->ref_count = 0;
+ return cc;
+}
+
+control_channel_t *add_virq(int virq)
+{
+ control_channel_t *cc;
+ int virq_port;
+
+ if (ctrl_chan_bind_virq(virq, &virq_port) == -1)
+ return NULL;
+
+ if ((cc_list[virq_port] != NULL) &&
+ (cc_list[virq_port]->type != CC_TYPE_VIRQ))
+ return NULL;
+
+ if ((cc_list[virq_port] != NULL) &&
+ (cc_list[virq_port]->type == CC_TYPE_VIRQ))
+ return cc_list[virq_port];
+
+ cc = (control_channel_t *)malloc(sizeof(control_channel_t));
+ if ( cc == NULL ) return NULL;
+
+ cc->type = CC_TYPE_VIRQ;
+ cc->local_port = virq_port;
+ cc->virq = virq;
+
+ return cc;
+}
+
+void get_interface(control_channel_t *cc)
+{
+ if (cc != NULL)
+ cc->ref_count++;
+}
+
+void put_interface(control_channel_t *cc)
+{
+ if (cc != NULL)
+ {
+ cc->ref_count--;
+ if (cc->ref_count <= 0)
+ {
+ DPRINTF("Freeing cc on port %d.\n", cc->local_port);
+ (void)evtchn_unbind(cc->local_port);
+ ctrl_chan_free(cc);
+ }
+ }
+}
+
+/* ------[ Simple helpers ]------------------------------------------------*/
+
+/* listen_socket() is straight from paul sheer's useful select_tut manpage. */
+static int listen_socket (int listen_port)
+{
+ struct sockaddr_in a;
+ int s;
+ int yes;
+
+ if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ perror ("socket");
+ return -1;
+ }
+
+ yes = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &yes, sizeof (yes)) < 0)
+ {
+ perror ("setsockopt");
+ close (s);
+ return -1;
+ }
+
+ memset (&a, 0, sizeof (a));
+ a.sin_port = htons (listen_port);
+ a.sin_family = AF_INET;
+ if (bind(s, (struct sockaddr *) &a, sizeof (a)) < 0)
+ {
+ perror ("bind");
+ close (s);
+ return -1;
+ }
+ printf ("accepting connections on port %d\n", (int) listen_port);
+ listen (s, 10);
+ return s;
+}
+
+/* ------[ Message handlers ]----------------------------------------------*/
+
+#define NO_CHANGE 0
+#define CONNECTED 1
+#define DISCONNECTED 2
+int handle_connect_msg( xcs_msg_t *msg, int fd )
+{
+ xcs_connect_msg_t *cmsg = &msg->u.connect;
+ connection_t *con;
+ int ret = NO_CHANGE;
+
+ switch (msg->type)
+ {
+ case XCS_CONNECT_CTRL:
+ {
+ if ( cmsg->session_id == 0 )
+ {
+ con = connection_new();
+ if ( con == NULL)
+ {
+ msg->result = XCS_RSLT_FAILED;
+ break;
+ }
+ msg->result = XCS_RSLT_OK;
+ cmsg->session_id = con->id;
+ con->ctrl_fd = fd;
+ ret = CONNECTED;
+ DPRINTF("New control connection\n");
+ break;
+ }
+
+ con = get_con_by_session(cmsg->session_id);
+ if ( con == NULL )
+ {
+ msg->result = XCS_RSLT_BADSESSION;
+ break;
+ }
+ if ( con->ctrl_fd != -1 )
+ {
+ msg->result = XCS_RSLT_CONINUSE;
+ break;
+ }
+ con->ctrl_fd = fd;
+ msg->result = XCS_RSLT_OK;
+ ret = CONNECTED;
+ DPRINTF("Rebound to control connection\n");
+ break;
+ }
+ case XCS_CONNECT_DATA:
+ {
+ con = get_con_by_session(cmsg->session_id);
+ if ( con == NULL )
+ {
+ msg->result = XCS_RSLT_BADSESSION;
+ break;
+ }
+ if ( con->data_fd != -1 )
+ {
+ msg->result = XCS_RSLT_CONINUSE;
+ break;
+ }
+ con->data_fd = fd;
+ msg->result = XCS_RSLT_OK;
+ ret = CONNECTED;
+ DPRINTF("Attached data connection\n");
+ break;
+
+ }
+ case XCS_CONNECT_BYE:
+ {
+ close ( fd );
+ ret = DISCONNECTED;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int handle_control_message( connection_t *con, xcs_msg_t *msg )
+{
+ int ret;
+ int reply_needed = 1;
+
+ DPRINTF("Got message, type %u.\n", msg->type);
+
+ switch (msg->type)
+ {
+ case XCS_MSG_BIND:
+ {
+ xcs_bind_msg_t *bmsg = &msg->u.bind;
+
+ if ( ! BIND_MSG_VALID(bmsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ ret = xcs_bind(con, bmsg->port, bmsg->type);
+ if (ret == 0) {
+ msg->result = XCS_RSLT_OK;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+ case XCS_MSG_UNBIND:
+ {
+ xcs_bind_msg_t *bmsg = &msg->u.bind;
+
+ if ( ! BIND_MSG_VALID(bmsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ ret = xcs_unbind(con, bmsg->port, bmsg->type);
+ if (ret == 0) {
+ msg->result = XCS_RSLT_OK;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+ case XCS_VIRQ_BIND:
+ {
+ control_channel_t *cc;
+ xcs_virq_msg_t *vmsg = &msg->u.virq;
+ if ( ! VIRQ_MSG_VALID(vmsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ cc = add_virq(vmsg->virq);
+ if (cc == NULL)
+ {
+ msg->result = XCS_RSLT_FAILED;
+ break;
+ }
+ ret = xcs_bind(con, cc->local_port, TYPE_VIRQ);
+ if (ret == 0) {
+ vmsg->port = cc->local_port;
+ msg->result = XCS_RSLT_OK;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+
+ case XCS_CIF_NEW_CC:
+ {
+ control_channel_t *cc;
+ xcs_interface_msg_t *imsg = &msg->u.interface;
+
+ if ( ! INTERFACE_MSG_VALID(imsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ cc = add_interface(imsg->dom, imsg->local_port, imsg->remote_port);
+ if (cc != NULL) {
+ get_interface(cc);
+ msg->result = XCS_RSLT_OK;
+ imsg->local_port = cc->local_port;
+ imsg->remote_port = cc->remote_port;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+
+ case XCS_CIF_FREE_CC:
+ {
+ control_channel_t *cc;
+ xcs_interface_msg_t *imsg = &msg->u.interface;
+
+ if ( ! INTERFACE_MSG_VALID(imsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ cc = add_interface(imsg->dom, imsg->local_port, imsg->remote_port);
+ if (cc != NULL) {
+ put_interface(cc);
+ }
+ msg->result = XCS_RSLT_OK;
+ break;
+ }
+ }
+ return reply_needed;
+}
+
+void handle_data_message( connection_t *con, xcs_msg_t *msg )
+{
+ control_channel_t *cc;
+ xcs_control_msg_t *cmsg = &msg->u.control;
+ int port;
+
+ switch (msg->type)
+ {
+ case XCS_REQUEST:
+ if ( cmsg->remote_dom > MAX_DOMS )
+ break;
+
+ port = dom_to_port(cmsg->remote_dom);
+ if (port == -1) break;
+ cc = cc_list[port];
+ if ((cc != NULL) && ( cc->type == CC_TYPE_INTERDOMAIN ))
+ {
+ DPRINTF("DN:REQ: dom:%d port: %d type: %d\n",
+ cc->remote_dom, cc->local_port,
+ cmsg->msg.type);
+ ctrl_chan_write_request(cc, cmsg);
+ ctrl_chan_notify(cc);
+ } else {
+ DPRINTF("tried to send a REQ to a null cc\n.");
+ }
+ break;
+
+ case XCS_RESPONSE:
+ if ( cmsg->remote_dom > MAX_DOMS )
+ break;
+
+ port = dom_to_port(cmsg->remote_dom);
+ if (port == -1) break;
+ cc = cc_list[port];
+ if ((cc != NULL) && ( cc->type == CC_TYPE_INTERDOMAIN ))
+ {
+ DPRINTF("DN:RSP: dom:%d port: %d type: %d\n",
+ cc->remote_dom, cc->local_port,
+ cmsg->msg.type);
+ ctrl_chan_write_response(cc, cmsg);
+ ctrl_chan_notify(cc);
+ }
+ break;
+
+ case XCS_VIRQ:
+ if ( !(PORT_VALID(cmsg->local_port)) )
+ break;
+
+ cc = cc_list[cmsg->local_port];
+
+ if ((cc != NULL) && ( cc->type == CC_TYPE_VIRQ ))
+ {
+ DPRINTF("DN:VIRQ: virq: %d port: %d\n",
+ cc->virq, cc->local_port);
+ ctrl_chan_notify(cc);
+ }
+ break;
+ }
+}
+
+/* ------[ Control interface handler ]-------------------------------------*/
+
+/* passed as a function pointer to the lookup. */
+void send_kmsg(connection_t *c, void *arg)
+{
+ xcs_msg_t *msg = (xcs_msg_t *)arg;
+
+ DPRINTF(" -> CONNECTION %d\n", c->data_fd);
+ if (c->data_fd > 0)
+ {
+ send(c->data_fd, msg, sizeof(xcs_msg_t), 0);
+ }
+}
+
+int handle_ctrl_if(void)
+{
+ control_channel_t *cc;
+ control_msg_t *msg;
+ xcs_msg_t kmsg;
+ int chan, ret;
+
+ DPRINTF("Event thread kicked!\n");
+again:
+ while ((chan = evtchn_read()) > 0)
+ {
+ evtchn_unmask(chan);
+ cc = cc_list[chan];
+ if (cc_list[chan] == NULL) {
+ DPRINTF("event from unknown channel (%d)\n", chan);
+ continue;
+ }
+
+ if ( cc_list[chan]->type == CC_TYPE_VIRQ )
+ {
+ DPRINTF("UP:VIRQ: virq:%d port: %d\n",
+ cc->virq, cc->local_port);
+ kmsg.type = XCS_VIRQ;
+ kmsg.u.control.local_port = cc->local_port;
+ xcs_lookup(cc->local_port, TYPE_VIRQ, send_kmsg, &kmsg);
+ continue;
+ }
+
+ while (ctrl_chan_request_to_read(cc))
+ {
+ msg = &kmsg.u.control.msg;
+ kmsg.type = XCS_REQUEST;
+ kmsg.u.control.remote_dom = cc->remote_dom;
+ kmsg.u.control.local_port = cc->local_port;
+ ret = ctrl_chan_read_request(cc, &kmsg.u.control);
+ DPRINTF("UP:REQ: dom:%d port: %d type: %d len: %d\n",
+ cc->remote_dom, cc->local_port,
+ msg->type, msg->length);
+ if (ret == 0)
+ xcs_lookup(cc->local_port, msg->type, send_kmsg, &kmsg);
+ }
+
+ while (ctrl_chan_response_to_read(cc))
+ {
+ msg = &kmsg.u.control.msg;
+ kmsg.type = XCS_RESPONSE;
+ kmsg.u.control.remote_dom = cc->remote_dom;
+ kmsg.u.control.local_port = cc->local_port;
+ ret = ctrl_chan_read_response(cc, &kmsg.u.control);
+ DPRINTF("UP:RSP: dom:%d port: %d type: %d len: %d\n",
+ cc->remote_dom, cc->local_port,
+ msg->type, msg->length);
+ if (ret == 0)
+ xcs_lookup(cc->local_port, msg->type, send_kmsg, &kmsg);
+ }
+ }
+
+ if (chan == -EINTR)
+ goto again;
+
+ return chan;
+}
+
+
+/* ------[ Main xcs code / big select loop ]-------------------------------*/
+
+
+typedef struct unbound_fd_st {
+ int fd;
+ struct timeval born;
+ struct unbound_fd_st *next;
+} unbound_fd_t;
+
+/* This makes ufd point to the next entry in the list, so need to *
+ * break/continue if called while iterating. */
+void delete_ufd(unbound_fd_t **ufd)
+{
+ unbound_fd_t *del_ufd;
+
+ del_ufd = *ufd;
+ *ufd = (*ufd)->next;
+ free( del_ufd );
+}
+
+void gc_ufd_list( unbound_fd_t **ufd )
+{
+ struct timeval now, delta;
+
+ gettimeofday(&now, NULL);
+
+ while ( *ufd != NULL )
+ {
+ timersub(&now, &(*ufd)->born, &delta);
+ if (delta.tv_sec > XCS_UFD_TIMEOUT)
+ {
+ DPRINTF("GC-UFD: closing fd: %d\n", (*ufd)->fd);
+ close((*ufd)->fd);
+ delete_ufd(ufd);
+ continue;
+ }
+ ufd = &(*ufd)->next;
+ }
+}
+
+int main (int argc, char*argv[])
+{
+ int listen_fd, evtchn_fd;
+ unbound_fd_t *unbound_fd_list = NULL, **ufd;
+ struct timeval timeout = { XCS_GC_INTERVAL, 0 };
+ connection_t **con;
+
+ /* Initialize xc and event connections. */
+ if (ctrl_chan_init() != 0)
+ {
+ printf("Couldn't open conneciton to libxc.\n");
+ exit(-1);
+ }
+
+ if ((evtchn_fd = evtchn_open()) < 0)
+ {
+ printf("Couldn't open event channel driver interface.\n");
+ exit(-1);
+ }
+
+ /* Initialize control interfaces, bindings. */
+ init_interfaces();
+ init_bindings();
+
+ listen_fd = listen_socket(XCS_TCP_PORT);
+
+ /* detach from our controlling tty so that a shell does hang waiting for
+ stopped jobs. */
+ /* we should use getopt() here */
+
+ if (!(argc == 2 && !strcmp(argv[1], "-i"))) {
+ pid_t pid = fork();
+ int fd;
+
+ if (pid == -1) {
+ perror("fork()");
+ } else if (pid) {
+ exit(0);
+ }
+
+ setsid();
+ close(2);
+ close(1);
+ close(0);
+ fd = open("/dev/null", O_RDWR);
+ dup(fd);
+ dup(fd);
+ }
+
+ for (;;)
+ {
+ int n, ret;
+ fd_set rd, wr, er;
+ FD_ZERO ( &rd );
+ FD_ZERO ( &wr );
+ FD_ZERO ( &er );
+
+ /* TCP listen fd: */
+ FD_SET ( listen_fd, &rd );
+ n = fd_max ( n, listen_fd );
+
+ /* Evtchn fd: */
+ FD_SET ( evtchn_fd, &rd );
+ n = fd_max ( n, evtchn_fd );
+
+ /* unbound connection fds: */
+ ufd = &unbound_fd_list;
+ while ((*ufd) != NULL)
+ {
+ FD_SET ( (*ufd)->fd, &rd );
+ n = fd_max ( n, (*ufd)->fd );
+ ufd = &(*ufd)->next;
+ }
+
+ /* control and data fds: */
+ con = &connection_list;
+ while ((*con) != NULL)
+ {
+ if ((*con)->ctrl_fd > 0)
+ {
+ FD_SET ( (*con)->ctrl_fd, &rd );
+ n = fd_max ( n, (*con)->ctrl_fd );
+ }
+ if ((*con)->data_fd > 0)
+ {
+ FD_SET ( (*con)->data_fd, &rd );
+ n = fd_max ( n, (*con)->data_fd );
+ }
+ con = &(*con)->next;
+ }
+
+ ret = select ( n + 1, &rd, &wr, &er, &timeout );
+
+ if ( (timeout.tv_sec == 0) && (timeout.tv_usec == 0) )
+ {
+ gc_ufd_list(&unbound_fd_list);
+ gc_connection_list();
+ timeout.tv_sec = XCS_GC_INTERVAL;
+ }
+
+ if ( (ret == -1) && (errno == EINTR) )
+ continue;
+ if ( ret < 0 )
+ {
+ perror ("select()");
+ exit(-1);
+ }
+
+ /* CASE 1: Events arriving on /dev/evtchn. */
+
+ if ( FD_ISSET (evtchn_fd, &rd ))
+ handle_ctrl_if();
+
+ /* CASE 2: New connection on the listen port. */
+ if ( FD_ISSET ( listen_fd, &rd ))
+ {
+ struct sockaddr_in remote_addr;
+ int size;
+ memset (&remote_addr, 0, sizeof (remote_addr));
+ size = sizeof remote_addr;
+ ret = accept(listen_fd, (struct sockaddr *)&remote_addr, &size);
+ if ( ret < 0 )
+ {
+ perror("accept()");
+ } else {
+ unbound_fd_t *new_ufd;
+
+ new_ufd = (unbound_fd_t *)malloc(sizeof(*new_ufd));
+
+ if (new_ufd != NULL)
+ {
+ gettimeofday(&new_ufd->born, NULL);
+ new_ufd->fd = ret;
+ new_ufd->next = unbound_fd_list;
+ unbound_fd_list = new_ufd;
+ } else {
+ perror("malloc unbound connection");
+ close(ret);
+ }
+ }
+ }
+
+ /* CASE 3a: Handle messages on control connections. */
+
+ con = &connection_list;
+ while ( *con != NULL )
+ {
+ if ( ((*con)->ctrl_fd > 0) && (FD_ISSET((*con)->ctrl_fd, &rd)) )
+ {
+ xcs_msg_t msg;
+ memset (&msg, 0, sizeof(msg));
+ ret = read( (*con)->ctrl_fd, &msg, sizeof(msg) );
+
+ if ( ret < 0 )
+ {
+ perror("reading ctrl fd.");
+ } else if ( ret == 0 )
+ {
+ DPRINTF("Control connection dropped.\n");
+ close ( (*con)->ctrl_fd );
+ (*con)->ctrl_fd = -1;
+ gettimeofday(&(*con)->disconnect_time, NULL);
+ } else
+ {
+ if ( ret != sizeof(msg) )
+ {
+ DPRINTF("Unexpected frame size!\n");
+ continue;
+ }
+
+ ret = handle_control_message( *con, &msg );
+
+ if ( ret == 1 )
+ send( (*con)->ctrl_fd, &msg, sizeof(msg), 0 );
+ }
+ }
+ con = &(*con)->next;
+ }
+
+ /* CASE 3b: Handle messages on data connections. */
+
+ con = &connection_list;
+ while ( *con != NULL )
+ {
+ if ( ((*con)->data_fd > 0) && (FD_ISSET((*con)->data_fd, &rd)) )
+ {
+ xcs_msg_t msg;
+ memset (&msg, 0, sizeof(msg));
+ ret = read( (*con)->data_fd, &msg, sizeof(msg) );
+
+ if ( ret < 0 )
+ {
+ perror("reading data fd.");
+ } else if ( ret == 0 )
+ {
+ DPRINTF("Data connection dropped.\n");
+ close ( (*con)->data_fd );
+ (*con)->data_fd = -1;
+ gettimeofday(&(*con)->disconnect_time, NULL);
+ } else
+ {
+ if ( ret != sizeof(msg) )
+ {
+ DPRINTF("Unexpected frame size!\n");
+ continue;
+ }
+
+ handle_data_message( *con, &msg );
+ }
+ }
+ con = &(*con)->next;
+ }
+
+ /* CASE 3c: Handle messages arriving on unbound connections. */
+ ufd = &unbound_fd_list;
+ while ((*ufd) != NULL)
+ {
+ if ( FD_ISSET( (*ufd)->fd, &rd ) )
+ {
+ xcs_msg_t msg;
+ memset (&msg, 0, sizeof(msg));
+ ret = read( (*ufd)->fd, &msg, sizeof(msg) );
+
+ if ( ret == 0 )
+ {
+ close ( (*ufd)->fd );
+ delete_ufd(ufd);
+ continue; /* we just advanced ufd */
+ } else {
+ if ( ret != sizeof(msg) )
+ {
+ DPRINTF("Unexpected frame size!\n");
+ continue;
+ }
+
+ ret = handle_connect_msg( &msg, (*ufd)->fd );
+
+ if ( (ret == CONNECTED) || (ret == NO_CHANGE) )
+ send( (*ufd)->fd, &msg, sizeof(msg), 0 );
+
+ if ( (ret = CONNECTED) || (ret = DISCONNECTED) )
+ {
+ delete_ufd( ufd );
+ continue;
+ }
+ }
+ }
+ ufd = &(*ufd)->next;
+ }
+ }
+}
+
diff --git a/tools/xcs/xcs.h b/tools/xcs/xcs.h
new file mode 100644
index 0000000000..545fc3d05f
--- /dev/null
+++ b/tools/xcs/xcs.h
@@ -0,0 +1,155 @@
+/* xcs.h
+ *
+ * public interfaces for the control interface switch (xcs).
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+
+#ifndef __XCS_H__
+#define __XCS_H__
+
+#include <pthread.h>
+#include <xc.h>
+#include <xen/xen.h>
+#include <xen/io/domain_controller.h>
+#include <xen/linux/privcmd.h>
+#include <sys/time.h>
+#include "xcs_proto.h"
+
+/* ------[ Debug macros ]--------------------------------------------------*/
+
+#if 0
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+/* ------[ XCS-specific defines and types ]--------------------------------*/
+
+#define MAX_DOMS 1024
+#define XCS_SESSION_TIMEOUT 10 /* (secs) disconnected session gc timeout */
+#define XCS_UFD_TIMEOUT 5 /* how long can connections be unbound? */
+#define XCS_GC_INTERVAL 5 /* How often to run gc handlers. */
+
+
+/* ------[ Other required defines ]----------------------------------------*/
+
+/* Size of a machine page frame. */
+#define PAGE_SIZE 4096
+
+#if defined(__i386__)
+#define rmb() __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define wmb() __asm__ __volatile__ ( "" : : : "memory" )
+#else
+#error "Define barriers"
+#endif
+
+#ifndef timersub /* XOPEN and __BSD don't cooperate well... */
+#define timersub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif /*timersub*/
+
+/* ------[ Bindings Interface ]--------------------------------------------*/
+
+/*forward declare connection_t */
+typedef struct connection_st connection_t;
+
+typedef struct {
+ int port;
+ u16 type;
+} binding_key_t;
+
+typedef struct binding_key_ent_st {
+ binding_key_t key;
+ struct binding_key_ent_st *next;
+} binding_key_ent_t;
+
+#define BINDING_KEYS_EQUAL(_k1, _k2) \
+ (((_k1)->port == (_k2)->port) && ((_k1)->type == (_k2)->type))
+
+int xcs_bind(connection_t *con, int port, u16 type);
+int xcs_unbind(connection_t *con, int port, u16 type);
+void xcs_lookup(int port, u16 type, void (*f)(connection_t *, void *),
+ void *arg);
+void init_bindings(void);
+
+/* ------[ Connection Interface ]------------------------------------------*/
+
+struct connection_st {
+ unsigned long id; /* Unique session id */
+ int ctrl_fd; /* TCP descriptors */
+ int data_fd; /* */
+ binding_key_ent_t *bindings; /* List of bindings */
+ connection_t *next; /* Linked list of connections */
+ struct timeval disconnect_time; /* " " */
+}; /* previously typedefed as connection_t */
+
+
+extern connection_t *connection_list;
+
+connection_t *get_con_by_session(unsigned long session_id);
+connection_t *connection_new();
+void connection_free(connection_t *con);
+int connection_add_binding(connection_t *con, binding_key_t *key);
+int connection_remove_binding(connection_t *con, binding_key_t *key);
+int connection_has_binding(connection_t *con, binding_key_t *key);
+void gc_connection_list(void);
+
+/* ------[ Control Channel Interfaces ]------------------------------------*/
+
+typedef struct {
+ int connected;
+ int ref_count;
+ int type;
+ u32 remote_dom;
+ int local_port;
+ int remote_port;
+ control_if_t *interface;
+ ctrl_back_ring_t tx_ring;
+ ctrl_front_ring_t rx_ring;
+ int virq;
+} control_channel_t;
+
+/* cc types that we care about */
+#define CC_TYPE_INTERDOMAIN 0
+#define CC_TYPE_VIRQ 1
+
+control_channel_t
+ *ctrl_chan_new(u32 dom, int local_port, int remote_port);
+void ctrl_chan_free(control_channel_t *cc);
+int ctrl_chan_init(void);
+int ctrl_chan_notify(control_channel_t *cc);
+int ctrl_chan_read_request(control_channel_t *cc, xcs_control_msg_t *);
+int ctrl_chan_write_request(control_channel_t *cc,
+ xcs_control_msg_t *smsg);
+int ctrl_chan_read_response(control_channel_t *cc, xcs_control_msg_t *);
+int ctrl_chan_write_response(control_channel_t *cc,
+ xcs_control_msg_t *smsg);
+int ctrl_chan_request_to_read(control_channel_t *cc);
+int ctrl_chan_space_to_write_request(control_channel_t *cc);
+int ctrl_chan_response_to_read(control_channel_t *cc);
+int ctrl_chan_space_to_write_response(control_channel_t *cc);
+int ctrl_chan_connect(control_channel_t *cc);
+void ctrl_chan_disconnect(control_channel_t *cc);
+int ctrl_chan_bind_virq(int virq, int *port);
+
+/* ------[ Event notification interfaces ]---------------------------------*/
+
+
+int evtchn_open(void);
+void evtchn_close();
+int evtchn_bind(int idx);
+int evtchn_unbind(int idx);
+void evtchn_unmask(u16 idx);
+int evtchn_read();
+
+#endif /* __XCS_H__ */
diff --git a/tools/xcs/xcs_proto.h b/tools/xcs/xcs_proto.h
new file mode 100644
index 0000000000..ea227c2ff7
--- /dev/null
+++ b/tools/xcs/xcs_proto.h
@@ -0,0 +1,101 @@
+/* xcs_proto.h
+ *
+ * protocol interfaces for the control interface switch (xcs).
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+#ifndef __XCS_PROTO_H__
+#define __XCS_PROTO_H__
+
+#define XCS_TCP_PORT 1633
+
+/* xcs message types: */
+#define XCS_CONNECT_CTRL 0 /* This is a control connection. */
+#define XCS_CONNECT_DATA 1 /* This is a data connection. */
+#define XCS_CONNECT_BYE 2 /* Terminate a session. */
+#define XCS_MSG_BIND 3 /* Register for a message type. */
+#define XCS_MSG_UNBIND 4 /* Unregister for a message type. */
+#define XCS_VIRQ_BIND 5 /* Register for a virq. */
+#define XCS_MSG_WRITELOCK 6 /* Writelock a (dom,type) pair. */
+#define XCS_CIF_NEW_CC 7 /* Create a new control channel. */
+#define XCS_CIF_FREE_CC 8 /* Create a new control channel. */
+#define XCS_REQUEST 9 /* This is a request message. */
+#define XCS_RESPONSE 10 /* this is a response Message. */
+#define XCS_VIRQ 11 /* this is a virq notification. */
+
+/* xcs result values: */
+#define XCS_RSLT_OK 0
+#define XCS_RSLT_FAILED 1 /* something bad happened. */
+#define XCS_RSLT_ARECONNECTED 2 /* attempt to over connect. */
+#define XCS_RSLT_BADSESSION 3 /* request for unknown session id. */
+#define XCS_RSLT_NOSESSION 4 /* tried to do something before NEW. */
+#define XCS_RSLT_CONINUSE 5 /* Requested connection is taken. */
+#define XCS_RSLT_BADREQUEST 6 /* Request message didn't validate. */
+
+/* Binding wildcards */
+#define PORT_WILDCARD 0xefffffff
+#define TYPE_WILDCARD 0xffff
+#define TYPE_VIRQ 0xfffe
+
+typedef struct {
+ u32 session_id;
+} xcs_connect_msg_t;
+
+typedef struct {
+ int port;
+ u16 type;
+} xcs_bind_msg_t;
+
+typedef struct {
+ int port;
+ u16 virq;
+} xcs_virq_msg_t;
+
+typedef struct {
+ u32 dom;
+ int local_port;
+ int remote_port;
+} xcs_interface_msg_t;
+
+typedef struct {
+ u32 remote_dom;
+ int local_port;
+ control_msg_t msg;
+} xcs_control_msg_t;
+
+typedef struct {
+ u32 type;
+ u32 result;
+ union {
+ xcs_connect_msg_t connect; /* These are xcs ctrl message types */
+ xcs_bind_msg_t bind;
+ xcs_virq_msg_t virq;
+ xcs_interface_msg_t interface;
+
+ xcs_control_msg_t control; /* These are xcs data message types */
+ } PACKED u;
+} xcs_msg_t;
+
+/* message validation macros. */
+#define PORT_VALID(_p) \
+ ( (((_p) >= 0) && ((_p) < NR_EVENT_CHANNELS)) \
+ || ((_p) == PORT_WILDCARD) )
+
+#define TYPE_VALID(_t) \
+ ( ((_t) < 256) \
+ || ((_t) == TYPE_VIRQ) \
+ || ((_t) == TYPE_WILDCARD) )
+
+#define BIND_MSG_VALID(_b) \
+ ( PORT_VALID((_b)->port) && TYPE_VALID((_b)->type) )
+
+/* Port is overwritten, and we don't currently validate the requested virq. */
+#define VIRQ_MSG_VALID(_v) ( 1 )
+
+/* Interfaces may return with ports of -1, but may not be requested as such */
+#define INTERFACE_MSG_VALID(_i) \
+ ( PORT_VALID((_i)->local_port) && PORT_VALID((_i)->remote_port) )
+
+#endif /* __XCS_PROTO_H__ */
diff --git a/tools/xcs/xcsdump.c b/tools/xcs/xcsdump.c
new file mode 100644
index 0000000000..dcfd2c9119
--- /dev/null
+++ b/tools/xcs/xcsdump.c
@@ -0,0 +1,182 @@
+/* xcsdump.c
+ *
+ * little tool to sniff control messages.
+ *
+ * Copyright (c) 2004, Andrew Warfield
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <xc.h>
+#include <xen/xen.h>
+#include <xen/io/domain_controller.h>
+#include "xcs_proto.h"
+#include "xcs.h"
+
+static int xcs_ctrl_fd = -1; /* connection to the xcs server. */
+static int xcs_data_fd = -1; /* connection to the xcs server. */
+
+int tcp_connect(char *ip, short port)
+{
+ struct sockaddr_in addr;
+ int ret, fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ printf("error creating xcs socket!\n");
+ return -1;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = inet_addr(ip);
+ memset(&(addr.sin_zero), '\0', 8);
+
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr));
+ if (ret < 0)
+ {
+ printf("error connecting to xcs!\n");
+ return -1;
+ }
+
+ return fd;
+}
+
+void tcp_disconnect(int *fd)
+{
+ close(*fd);
+ *fd = -1;
+}
+
+void xcs_read(int fd, xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = read(fd, msg, sizeof(xcs_msg_t));
+ if (ret != sizeof(xcs_msg_t)) {
+ printf("read error\n");
+ exit(-1);
+ }
+}
+
+void xcs_send(int fd, xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = send(fd, msg, sizeof(xcs_msg_t), 0);
+ if (ret != sizeof(xcs_msg_t) )
+ {
+ printf("send error\n");
+ exit(-1);
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ int ret, i;
+ xcs_msg_t msg;
+ control_msg_t *cmsg;
+ int verbose = 0;
+
+ if (argc > 1)
+ if ((strlen(argv[1]) >=2) && (strncmp(argv[1], "-v", 2) == 0))
+ verbose = 1;
+
+ ret = tcp_connect("127.0.0.1", XCS_TCP_PORT);
+ if (ret < 0)
+ {
+ printf("connect failed!\n");
+ exit(-1);
+ }
+ xcs_ctrl_fd = ret;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.type = XCS_CONNECT_CTRL;
+ xcs_send(xcs_ctrl_fd, &msg);
+ xcs_read(xcs_ctrl_fd, &msg);
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("Error connecting control channel\n");
+ exit(-1);
+ }
+
+ ret = tcp_connect("127.0.0.1", XCS_TCP_PORT);
+ if (ret < 0)
+ {
+ printf("connect failed!\n");
+ exit(-1);
+ }
+ xcs_data_fd = ret;
+
+ msg.type = XCS_CONNECT_DATA;
+ /* session id is set from before... */
+ xcs_send(xcs_data_fd, &msg);
+ xcs_read(xcs_data_fd, &msg);
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("Error connecting data channel\n");
+ exit(-1);
+ }
+
+ msg.type = XCS_MSG_BIND;
+ msg.u.bind.port = PORT_WILDCARD;
+ msg.u.bind.type = TYPE_WILDCARD;
+ xcs_send(xcs_ctrl_fd, &msg);
+ xcs_read(xcs_ctrl_fd, &msg);
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("Error binding.\n");
+ exit(-1);
+ }
+
+
+ while (1)
+ {
+ xcs_read(xcs_data_fd, &msg);
+ cmsg = &msg.u.control.msg;
+
+ for (i=0; i<60; i++)
+ if ((!isprint(cmsg->msg[i])) && (cmsg->msg[i] != '\0'))
+ cmsg->msg[i] = '.';
+ cmsg->msg[59] = '\0';
+
+ switch (msg.type)
+ {
+ case XCS_REQUEST:
+ printf("[REQUEST ] : (dom:%u port:%d) (type:(%d,%d) len %d) \n",
+ msg.u.control.remote_dom,
+ msg.u.control.local_port,
+ msg.u.control.msg.type,
+ msg.u.control.msg.subtype,
+ msg.u.control.msg.length);
+ if (verbose)
+ printf(" : %s\n", msg.u.control.msg.msg);
+ break;
+ case XCS_RESPONSE:
+ printf("[RESPONSE] : (dom:%u port:%d) (type:(%d,%d) len %d) \n",
+ msg.u.control.remote_dom,
+ msg.u.control.local_port,
+ msg.u.control.msg.type,
+ msg.u.control.msg.subtype,
+ msg.u.control.msg.length);
+ if (verbose)
+ printf(" : %s\n", msg.u.control.msg.msg);
+ break;
+ case XCS_VIRQ:
+ printf("[VIRQ ] : %d\n", msg.u.control.local_port);
+ default:
+ printf("[UNKNOWN ]\n");
+ }
+ }
+
+ return(0);
+}
diff --git a/tools/xentrace/Makefile b/tools/xentrace/Makefile
index de5350e3c2..5ed26cf795 100644
--- a/tools/xentrace/Makefile
+++ b/tools/xentrace/Makefile
@@ -1,12 +1,14 @@
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
+INSTALL_DATA = $(INSTALL) -m0644
XEN_ROOT=../..
-include $(XEN_ROOT)/tools/Make.defs
+include $(XEN_ROOT)/tools/Rules.mk
CC = gcc
-CFLAGS = -Wall -Werror -O3
+CFLAGS += -Wall -Werror -O3
-CFLAGS += -I $(XEN_HYPERVISOR_IFS)
-CFLAGS += -I $(XEN_LINUX_INCLUDE)
CFLAGS += -I $(XEN_XC)
CFLAGS += -I $(XEN_LIBXC)
CFLAGS += -I $(XEN_LIBXUTIL)
@@ -22,12 +24,14 @@ MAN8 = $(wildcard *.8)
all: $(BIN)
install: all
- mkdir -p $(prefix)/usr/bin
- mkdir -p $(prefix)/usr/man/man1
- mkdir -p $(prefix)/usr/man/man8
- install -m0755 $(BIN) $(SCRIPTS) $(prefix)/usr/bin
- install -m0644 $(MAN1) $(prefix)/usr/man/man1
- install -m0644 $(MAN8) $(prefix)/usr/man/man8
+ [ -d $(DESTDIR)/usr/bin ] || $(INSTALL_DIR) $(DESTDIR)/usr/bin
+ [ -d $(DESTDIR)/usr/man/man1 ] || \
+ $(INSTALL_DIR) $(DESTDIR)/usr/man/man1
+ [ -d $(DESTDIR)/usr/man/man8 ] || \
+ $(INSTALL_DIR) $(DESTDIR)/usr/man/man8
+ $(INSTALL_PROG) $(BIN) $(SCRIPTS) $(DESTDIR)/usr/bin
+ $(INSTALL_DATA) $(MAN1) $(DESTDIR)/usr/man/man1
+ $(INSTALL_DATA) $(MAN8) $(DESTDIR)/usr/man/man8
clean:
$(RM) *.a *.so *.o *.rpm $(BIN)
diff --git a/tools/xentrace/xentrace.c b/tools/xentrace/xentrace.c
index 9972422878..351ecd71a8 100644
--- a/tools/xentrace/xentrace.c
+++ b/tools/xentrace/xentrace.c
@@ -22,9 +22,7 @@
#include <signal.h>
#include "xc_private.h"
-
-/* from xen/include/hypervisor-ifs */
-#include <trace.h>
+#include <xen/trace.h>
extern FILE *stderr;
@@ -85,13 +83,13 @@ void write_rec(unsigned int cpu, struct t_rec *rec, FILE *out)
/**
* get_tbufs - get pointer to and size of the trace buffers
- * @phys_addr: location to store physical address if the trace buffers to
+ * @mach_addr: location to store machine address if the trace buffers to
* @size: location to store the size of a trace buffer to
*
- * Gets the physical address of the trace pointer area and the size of the
+ * Gets the machine address of the trace pointer area and the size of the
* per CPU buffers.
*/
-void get_tbufs(unsigned long *phys_addr, unsigned long *size)
+void get_tbufs(unsigned long *mach_addr, unsigned long *size)
{
int ret;
dom0_op_t op; /* dom0 op we'll build */
@@ -110,40 +108,39 @@ void get_tbufs(unsigned long *phys_addr, unsigned long *size)
exit(EXIT_FAILURE);
}
- *phys_addr = op.u.gettbufs.phys_addr;
+ *mach_addr = op.u.gettbufs.mach_addr;
*size = op.u.gettbufs.size;
}
/**
* map_tbufs - memory map Xen trace buffers into user space
- * @tbufs: physical address of the trace buffers
+ * @tbufs: machine address of the trace buffers
* @num: number of trace buffers to map
* @size: size of each trace buffer
*
- * Maps the Xen trace buffers them into process address space by memory mapping
- * /dev/mem. Returns the location the buffers have been mapped to.
+ * Maps the Xen trace buffers them into process address space.
*/
-struct t_buf *map_tbufs(unsigned long tbufs_phys, unsigned int num,
+struct t_buf *map_tbufs(unsigned long tbufs_mach, unsigned int num,
unsigned long size)
{
- int dm_fd; /* file descriptor for /dev/mem */
+ int xc_handle; /* file descriptor for /proc/xen/privcmd */
struct t_buf *tbufs_mapped;
- dm_fd = open("/dev/mem", O_RDONLY);
+ xc_handle = xc_interface_open();
- if ( dm_fd < 0 )
+ if ( xc_handle < 0 )
{
- PERROR("Open /dev/mem when mapping trace buffers\n");
+ PERROR("Open /proc/xen/privcmd when mapping trace buffers\n");
exit(EXIT_FAILURE);
}
- tbufs_mapped = (struct t_buf *)mmap(NULL, size * num,
- PROT_READ, MAP_SHARED,
- dm_fd, (off_t)tbufs_phys);
+ tbufs_mapped = xc_map_foreign_range(xc_handle, 0 /* Dom 0 ID */,
+ size * num, PROT_READ,
+ tbufs_mach >> PAGE_SHIFT);
- close(dm_fd);
+ xc_interface_close(xc_handle);
- if ( tbufs_mapped == MAP_FAILED )
+ if ( tbufs_mapped == 0 )
{
PERROR("Failed to mmap trace buffers");
exit(EXIT_FAILURE);
@@ -187,16 +184,16 @@ struct t_buf **init_bufs_ptrs(void *bufs_mapped, unsigned int num,
/**
* init_rec_ptrs - initialises data area pointers to locations in user space
- * @tbufs_phys: physical base address of the trace buffer area
+ * @tbufs_mach: machine base address of the trace buffer area
* @tbufs_mapped: user virtual address of base of trace buffer area
* @meta: array of user-space pointers to struct t_buf's of metadata
* @num: number of trace buffers
*
* Initialises data area pointers to the locations that data areas have been
- * mapped in user space. Note that the trace buffer metadata contains physical
+ * mapped in user space. Note that the trace buffer metadata contains machine
* pointers - the array returned allows more convenient access to them.
*/
-struct t_rec **init_rec_ptrs(unsigned long tbufs_phys,
+struct t_rec **init_rec_ptrs(unsigned long tbufs_mach,
struct t_buf *tbufs_mapped,
struct t_buf **meta,
unsigned int num)
@@ -212,8 +209,8 @@ struct t_rec **init_rec_ptrs(unsigned long tbufs_phys,
}
for ( i = 0; i<num; i++ )
- data[i] = (struct t_rec *)((unsigned long)meta[i]->data -
- tbufs_phys + (unsigned long)tbufs_mapped);
+ data[i] = (struct t_rec *)(meta[i]->data - tbufs_mach
+ + (unsigned long)tbufs_mapped);
return data;
}
@@ -283,7 +280,7 @@ int monitor_tbufs(FILE *logfile)
struct t_rec **data; /* pointers to the trace buffer data areas
* where they are mapped into user space. */
unsigned long *cons; /* store tail indexes for the trace buffers */
- unsigned long tbufs_phys; /* physical address of the tbufs */
+ unsigned long tbufs_mach; /* machine address of the tbufs */
unsigned int num; /* number of trace buffers / logical CPUS */
unsigned long size; /* size of a single trace buffer */
@@ -293,36 +290,26 @@ int monitor_tbufs(FILE *logfile)
num = get_num_cpus();
/* setup access to trace buffers */
- get_tbufs(&tbufs_phys, &size);
- tbufs_mapped = map_tbufs(tbufs_phys, num, size);
+ get_tbufs(&tbufs_mach, &size);
+ tbufs_mapped = map_tbufs(tbufs_mach, num, size);
size_in_recs = (size / sizeof(struct t_rec) )-1;
/* build arrays of convenience ptrs */
meta = init_bufs_ptrs (tbufs_mapped, num, size);
- data = init_rec_ptrs (tbufs_phys, tbufs_mapped, meta, num);
+ data = init_rec_ptrs (tbufs_mach, tbufs_mapped, meta, num);
cons = init_tail_idxs (meta, num);
/* now, scan buffers for events */
while ( !interrupted )
{
for ( i = 0; ( i < num ) && !interrupted; i++ )
- {
-/* printf("XX%d: cons=%ld head=%ld %p\n", i,
- cons[i], meta[i]->head, data[i] + (cons[i] % size_in_recs) );
- */
while( cons[i] != meta[i]->head )
{
-/*
- if( (cons[i] % 6 ) == 0 )
- printf("%d: cons=%ld head=%ld %p\n", i,
- cons[i], meta[i]->head, data[i] + (cons[i] % size_in_recs) );
- */
write_rec(i, data[i] + (cons[i] % size_in_recs), logfile);
cons[i]++;
}
- }
nanosleep(&opts.poll_sleep, NULL);
}
diff --git a/tools/xfrd/Make.xfrd b/tools/xfrd/Make.xfrd
index bb746d6bef..3bbfe4d5d6 100644
--- a/tools/xfrd/Make.xfrd
+++ b/tools/xfrd/Make.xfrd
@@ -26,7 +26,6 @@ UTIL_LIB_SRC += xdr.c
XFRD_PROG_SRC =
XFRD_PROG_SRC += xfrd.c
-#XFRD_PROG_SRC += xfr_msg.c
XFRD_PROG_SRC += xen_domain.c
XFRD_PROG_SRC += select.c
XFRD_PROG_SRC += connection.c
diff --git a/tools/xfrd/Makefile b/tools/xfrd/Makefile
index 5824960ac9..4cdc810e95 100644
--- a/tools/xfrd/Makefile
+++ b/tools/xfrd/Makefile
@@ -4,17 +4,15 @@
# Mike Wray <mike.wray@hp.com>
#============================================================================
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
+
XEN_ROOT = ../..
-include $(XEN_ROOT)/tools/Make.defs
+include $(XEN_ROOT)/tools/Rules.mk
XFRD_INSTALL_DIR = /usr/sbin
-vpath %.h $(XEN_HYPERVISOR_IFS)
-INCLUDES += -I $(XEN_HYPERVISOR_IFS)
-
-vpath %h $(XEN_LINUX_INCLUDE)
-INCLUDES += -I $(XEN_LINUX_INCLUDE)
-
vpath %.h $(XEN_LIBXC)
INCLUDES += -I $(XEN_LIBXC)
@@ -32,11 +30,13 @@ XFRD_PROG_OBJ += $(UTIL_LIB)
# Define to use stubs, undefine to use the real Xen functions.
#CPPFLAGS += -D _XEN_XFR_STUB_
+ifeq ($(SXPR_DEBUG),1)
+CPPFLAGS += -D _XEN_XFR_STUB_ -D SXPR_PARSER_MAIN
+endif
+
CC := gcc
-CFLAGS += -g
-CFLAGS += -Wall
-CFALGS += -Werror
+CFLAGS += -Wall -Werror -O3 -fno-strict-aliasing
CFLAGS += $(INCLUDES)
# Make gcc generate dependencies.
CFLAGS += -Wp,-MD,.$(@F).d
@@ -70,8 +70,9 @@ xfrd: $(XFRD_PROG_OBJ)
.PHONY: install
install: xfrd
- mkdir -p $(prefix)/$(XFRD_INSTALL_DIR)
- install -m 0755 xfrd $(prefix)/$(XFRD_INSTALL_DIR)
+ [ -d $(DESTDIR)$(XFRD_INSTALL_DIR) ] || \
+ $(INSTALL_DIR) $(DESTDIR)$(XFRD_INSTALL_DIR)
+ $(INSTALL_PROG) xfrd $(DESTDIR)$(XFRD_INSTALL_DIR)
.PHONY: libutil
libutil: $(UTIL_LIB)
diff --git a/tools/xfrd/debug.h b/tools/xfrd/debug.h
index 69a6b49937..3df5345095 100644
--- a/tools/xfrd/debug.h
+++ b/tools/xfrd/debug.h
@@ -45,20 +45,21 @@
#else
#include <stdio.h>
+#include <unistd.h>
#ifdef DEBUG
-#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
-#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
-#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
-#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define dprintf(fmt, args...) fprintf(stdout, "%d [DBG] " MODULE_NAME ">%s" fmt, getpid(), __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "%d [WRN] " MODULE_NAME ">%s" fmt, getpid(),__FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "%d [INF] " MODULE_NAME ">%s" fmt, getpid(),__FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "%d [ERR] " MODULE_NAME ">%s" fmt, getpid(),__FUNCTION__, ##args)
#else
#define dprintf(fmt, args...) do {} while(0)
-#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args)
-#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args)
-#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "%d [WRN] " MODULE_NAME fmt, getpid(), ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "%d [INF] " MODULE_NAME fmt, getpid(), ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "%d [ERR] " MODULE_NAME fmt, getpid(), ##args)
#endif
diff --git a/tools/xfrd/http.h b/tools/xfrd/http.h
new file mode 100644
index 0000000000..711ccc9787
--- /dev/null
+++ b/tools/xfrd/http.h
@@ -0,0 +1,50 @@
+#ifndef _XFRD_HTTP_H_
+#define _XFRD_HTTP_H_
+
+enum {
+ HTTP_OK = 200,
+ HTTP_CREATED = 201,
+ HTTP_ACCEPTED = 202,
+ HTTP_NON_AUTHORITATIVE_INFORMATION = 203,
+ HTTP_NO_CONTENT = 204,
+ HTTP_RESET_CONTENT = 205,
+ HTTP_PARTIAL_CONTENT = 206,
+ HTTP_MULTI_STATUS = 207,
+
+ HTTP_MULTIPLE_CHOICE = 300,
+ HTTP_MOVED_PERMANENTLY = 301,
+ HTTP_FOUND = 302,
+ HTTP_SEE_OTHER = 303,
+ HTTP_NOT_MODIFIED = 304,
+ HTTP_USE_PROXY = 305,
+ HTTP_TEMPORARY_REDIRECT = 307,
+
+ HTTP_BAD_REQUEST = 400,
+ HTTP_UNAUTHORIZED = 401,
+ HTTP_PAYMENT_REQUIRED = 402,
+ HTTP_FORBIDDEN = 403,
+ HTTP_NOT_FOUND = 404,
+ HTTP_NOT_ALLOWED = 405,
+ HTTP_NOT_ACCEPTABLE = 406,
+ HTTP_PROXY_AUTH_REQUIRED = 407,
+ HTTP_REQUEST_TIMEOUT = 408,
+ HTTP_CONFLICT = 409,
+ HTTP_GONE = 410,
+ HTTP_LENGTH_REQUIRED = 411,
+ HTTP_PRECONDITION_FAILED = 412,
+ HTTP_REQUEST_ENTITY_TOO_LARGE = 413,
+ HTTP_REQUEST_URI_TOO_LONG = 414,
+ HTTP_UNSUPPORTED_MEDIA_TYPE = 415,
+ HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
+ HTTP_EXPECTATION_FAILED = 417,
+
+ HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_NOT_IMPLEMENTED = 501,
+ HTTP_BAD_GATEWAY = 502,
+ HTTP_SERVICE_UNAVAILABLE = 503,
+ HTTP_GATEWAY_TIMEOUT = 504,
+ HTTP_VERSION_NOT_SUPPORTED = 505,
+ HTTP_INSUFFICIENT_STORAGE_SPACE = 507,
+ HTTP_NOT_EXTENDED = 510,
+};
+#endif /* ! _XFRD_HTTP_H_ */
diff --git a/tools/xfrd/xen_domain.c b/tools/xfrd/xen_domain.c
index a3e270518e..64567900b4 100644
--- a/tools/xfrd/xen_domain.c
+++ b/tools/xfrd/xen_domain.c
@@ -16,10 +16,10 @@ typedef unsigned long u32;
#define MODULE_NAME "XFRD"
#define DEBUG 1
+#undef DEBUG
#include "debug.h"
-
-int domain_suspend(u32 dom, void *data){
+int domain_suspend(void *data, u32 dom){
int err = 0;
Conn *xend = data;
@@ -29,6 +29,10 @@ int domain_suspend(u32 dom, void *data){
return err;
}
+int domain_configure(void *data, u32 dom, char *vmconfig, int vmconfig_n){
+ return xen_domain_configure(dom, vmconfig, vmconfig_n);
+}
+
#ifndef _XEN_XFR_STUB_
static int xc_handle = 0;
@@ -53,7 +57,10 @@ void xcfini(void){
* At some point during this the domain is suspended, and then there's no way back.
* Even if something later goes wrong we can't restart the domain.
*/
-int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n){
+int xen_domain_snd(Conn *xend, IOStream *io,
+ uint32_t dom,
+ char *vmconfig, int vmconfig_n,
+ int live, int resource){
int err = 0;
#ifdef _XEN_XFR_STUB_
char buf[1024];
@@ -76,19 +83,23 @@ int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int v
}
dom = 99;
- err = domain_suspend(dom, xend);
+ err = domain_suspend(xend, dom);
IOStream_close(io);
exit:
#else
XcIOContext _ioctxt = {}, *ioctxt = &_ioctxt;
- dprintf("> dom=%d\n", dom);
ioctxt->domain = dom;
ioctxt->io = io;
ioctxt->info = iostdout;
ioctxt->err = iostderr;
ioctxt->data = xend;
ioctxt->suspend = domain_suspend;
-
+ ioctxt->vmconfig = vmconfig;
+ ioctxt->vmconfig_n = vmconfig_n;
+ if(live){
+ ioctxt->flags |= XCFLAGS_LIVE;
+ }
+ ioctxt->resource = resource;
err = xc_linux_save(xcinit(), ioctxt);
#endif
dprintf("< err=%d\n", err);
@@ -98,7 +109,10 @@ int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int v
/** Receive domain state.
* Create a new domain and store the received state into it.
*/
-int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n){
+int xen_domain_rcv(IOStream *io,
+ uint32_t *dom,
+ char **vmconfig, int *vmconfig_n,
+ int *configured){
int err = 0;
#ifdef _XEN_XFR_STUB_
char buf[1024];
@@ -124,51 +138,189 @@ int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n
ioctxt->io = io;
ioctxt->info = iostdout;
ioctxt->err = iostderr;
+ ioctxt->configure = domain_configure;
+ if ( !*configured )
+ ioctxt->flags |= XCFLAGS_CONFIGURE;
err = xc_linux_restore(xcinit(), ioctxt);
+ *dom = ioctxt->domain;
+ *vmconfig = ioctxt->vmconfig;
+ *vmconfig_n = ioctxt->vmconfig_n;
+ *configured = (ioctxt->flags & XCFLAGS_CONFIGURE);
#endif
dprintf("< err=%d\n", err);
return err;
}
#include <curl/curl.h>
+#include "http.h"
+/** Flag indicating whether we need to initialize libcurl.
+ */
static int do_curl_global_init = 1;
+/** Get a curl handle, initializing libcurl if needed.
+ *
+ * @return curl handle
+ */
static CURL *curlinit(void){
if(do_curl_global_init){
do_curl_global_init = 0;
+ // Stop libcurl using the proxy. There's a curl option to
+ // set the proxy - but no option to defeat it.
+ unsetenv("http_proxy");
curl_global_init(CURL_GLOBAL_ALL);
}
return curl_easy_init();
}
-/** Configure a new domain. Talk to xend using libcurl.
+/** Curl debug function.
*/
-int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n){
+int curldebug(CURL *curl, curl_infotype ty, char *buf, size_t buf_n, void *data){
+ printf("%*s\n", buf_n, buf);
+ return 0;
+}
+
+/** Setup a curl handle with a url.
+ * Creates the url by formatting 'fmt' and the remaining arguments.
+ *
+ * @param pcurl return parameter for the curl handle
+ * @param url url buffer
+ * @param url_n size of url
+ * @param fmt url format string, followed by parameters
+ * @return 0 on success, error code otherwise
+ */
+static int curlsetup(CURL **pcurl, struct curl_slist **pheaders, char *url, int url_n, char *fmt, ...){
int err = 0;
+ va_list args;
CURL *curl = NULL;
- CURLcode curlcode = 0;
- char domainurl[128] = {};
- int domainurl_n = sizeof(domainurl) - 1;
- int n;
- struct curl_httppost *form = NULL, *last = NULL;
- CURLFORMcode formcode = 0;
+ struct curl_slist *headers = NULL;
+ int n = 0;
- dprintf("> dom=%u\n", dom);
curl = curlinit();
if(!curl){
eprintf("> Could not init libcurl\n");
err = -ENOMEM;
goto exit;
}
- n = snprintf(domainurl, domainurl_n,
- "http://localhost:%d/xend/domain/%u", XEND_PORT, dom);
- if(n <= 0 || n >= domainurl_n){
+ url_n -= 1;
+ va_start(args, fmt);
+ n = vsnprintf(url, url_n, fmt, args);
+ va_end(args);
+ if(n <= 0 || n >= url_n){
err = -ENOMEM;
- eprintf("Out of memory in url.\n");
+ eprintf("> Out of memory in url\n");
+ goto exit;
+ }
+ dprintf("> url=%s\n", url);
+#if DEBUG
+ // Verbose.
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
+ // Call the debug function on data received.
+ curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curldebug);
+#else
+ // No progress meter.
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
+ // Completely quiet.
+ curl_easy_setopt(curl, CURLOPT_MUTE, 1);
+#endif
+ // Set the URL.
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+
+ headers = curl_slist_append(headers, "Expect:");
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+
+ exit:
+ if(err && curl){
+ curl_easy_cleanup(curl);
+ curl = NULL;
+ }
+ *pcurl = curl;
+ if (pheaders)
+ *pheaders = headers;
+ return err;
+}
+
+static void curlcleanup(CURL **pcurl, struct curl_slist **pheaders){
+ if (*pcurl)
+ curl_easy_cleanup(*pcurl);
+ if (*pheaders)
+ curl_slist_free_all(*pheaders);
+ *pcurl = NULL;
+ *pheaders = NULL;
+}
+/** Make the http request stored in the curl handle and get
+ * the result code from the curl code and the http return code.
+ *
+ * @param curl curl handle
+ * @return 0 for success, error code otherwise
+ */
+int curlresult(CURL *curl){
+ int err = 0;
+ CURLcode curlcode = 0;
+ long httpcode = 0;
+
+ curlcode = curl_easy_perform(curl);
+ if(curlcode){
+ eprintf("> curlcode=%d\n", curlcode);
+ err = -EINVAL;
goto exit;
}
+ curl_easy_getinfo(curl, CURLINFO_HTTP_CODE, &httpcode);
+ if(httpcode != HTTP_OK){
+ eprintf("> httpcode=%d\n", (int)httpcode);
+ err = -EINVAL;
+ goto exit;
+ }
+ exit:
+ return err;
+}
+
+/** Get xend to list domains.
+ * We use this to force xend to refresh its domain list.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int xen_domain_ls(void){
+ int err = 0;
+ CURL *curl = NULL;
+ struct curl_slist *headers = NULL;
+ char url[128] = {};
+ int url_n = sizeof(url);
+
+ dprintf(">\n");
+ err = curlsetup(&curl, &headers, url, url_n, "http://localhost:%d/xend/domain", XEND_PORT);
+ if(err) goto exit;
+ err = curlresult(curl);
+ exit:
+ curlcleanup(&curl, &headers);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Get xend to configure a new domain.
+ *
+ * @param dom domain id
+ * @param vmconfig configuration string
+ * @param vmconfig_n length of vmconfig
+ * @return 0 on success, error code otherwise
+ */
+int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n){
+ int err = 0;
+ CURL *curl = NULL;
+ struct curl_slist *headers = NULL;
+ char url[128] = {};
+ int url_n = sizeof(url);
+ struct curl_httppost *form = NULL, *last = NULL;
+ CURLFORMcode formcode = 0;
+
+ dprintf("> dom=%u\n", dom);
+ // List domains so that xend will update its domain list and notice the new domain.
+ xen_domain_ls();
+
+ err = curlsetup(&curl, &headers, url, url_n, "http://localhost:%d/xend/domain/%u", XEND_PORT, dom);
+ if(err) goto exit;
+
// Config field - set from vmconfig.
formcode = curl_formadd(&form, &last,
CURLFORM_COPYNAME, "config",
@@ -186,37 +338,56 @@ int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n){
CURLFORM_COPYNAME, "op",
CURLFORM_COPYCONTENTS, "configure",
CURLFORM_END);
-
if(formcode){
eprintf("> Error adding op field.\n");
+ err = -EINVAL;
goto exit;
}
- // No progress meter.
- //curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
- // Completely quiet.
- //curl_easy_setopt(curl, CURLOPT_MUTE, 1);
- // Set the URL.
- curl_easy_setopt(curl, CURLOPT_URL, domainurl);
// POST the form.
curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
- dprintf("> curl perform...\n");
-#if 0 && defined(_XEN_XFR_STUB_)
- dprintf("> _XEN_XFR_STUB_ defined - not calling xend\n");
- curlcode = 0;
-#else
- curlcode = curl_easy_perform(curl);
-#endif
+ err = curlresult(curl);
exit:
- if(curl) curl_easy_cleanup(curl);
+ curlcleanup(&curl, &headers);
if(form) curl_formfree(form);
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Get xend to unpause a domain.
+ *
+ * @param dom domain id
+ * @return 0 on success, error code otherwise
+ */
+int xen_domain_unpause(uint32_t dom){
+ int err = 0;
+ CURL *curl = NULL;
+ struct curl_slist *headers = NULL;
+ char url[128] = {};
+ int url_n = sizeof(url);
+ struct curl_httppost *form = NULL, *last = NULL;
+ CURLFORMcode formcode = 0;
+
+ dprintf("> dom=%u\n", dom);
+
+ err = curlsetup(&curl, &headers, url, url_n, "http://localhost:%d/xend/domain/%u", XEND_PORT, dom);
+ if(err) goto exit;
+
+ // Op field.
+ formcode = curl_formadd(&form, &last,
+ CURLFORM_COPYNAME, "op",
+ CURLFORM_COPYCONTENTS, "unpause",
+ CURLFORM_END);
if(formcode){
- dprintf("> formcode=%d\n", formcode);
- err = -EINVAL;
- }
- if(curlcode){
- dprintf("> curlcode=%d\n", curlcode);
+ eprintf("> Error adding op field.\n");
err = -EINVAL;
+ goto exit;
}
+ // POST the form.
+ curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
+ err = curlresult(curl);
+ exit:
+ curlcleanup(&curl, &headers);
+ if(form) curl_formfree(form);
dprintf("< err=%d\n", err);
return err;
}
diff --git a/tools/xfrd/xen_domain.h b/tools/xfrd/xen_domain.h
index 4618c642b7..c84e8b8d63 100644
--- a/tools/xfrd/xen_domain.h
+++ b/tools/xfrd/xen_domain.h
@@ -7,9 +7,16 @@
/** Define to use stubs. Undefine to use Xen ops. */
//#define _XEN_XFR_STUB_
-extern int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n);
-extern int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n);
+extern int xen_domain_snd(Conn *xend, IOStream *io,
+ uint32_t dom,
+ char *vmconfig, int vmconfig_n,
+ int live, int resource);
+extern int xen_domain_rcv(IOStream *io,
+ uint32_t *dom,
+ char **vmconfig, int *vmconfig_n,
+ int *configured);
extern int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n);
+extern int xen_domain_unpause(uint32_t dom);
#endif
diff --git a/tools/xfrd/xfrd.c b/tools/xfrd/xfrd.c
index 301674b1fe..136a614aa3 100644
--- a/tools/xfrd/xfrd.c
+++ b/tools/xfrd/xfrd.c
@@ -19,6 +19,8 @@
#include <getopt.h>
#include <errno.h>
#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
@@ -33,6 +35,7 @@
#include "file_stream.h"
#include "string_stream.h"
#include "lzi_stream.h"
+#include "gzip_stream.h"
#include "sys_net.h"
#include "sys_string.h"
@@ -47,6 +50,7 @@
#define MODULE_NAME "XFRD"
#define DEBUG 1
+#undef DEBUG
#include "debug.h"
/*
@@ -78,7 +82,7 @@ receiver:
(xfr.hello <major> <minor>)
(xfr.err <code> <reason>)
- xend->xfrd (xfr.migrate <domain> <vmconfig> <host> <port>)
+ xend->xfrd (xfr.migrate <domain> <vmconfig> <host> <port> <live>)
(xfr.save <domain> <vmconfig> <file>)
xfrd->xend (xfr.suspend <domain>)
xfrd->xend (xfr.progress <percent> <rate: kb/s>)
@@ -92,11 +96,14 @@ receiver:
Sxpr oxfr_configure; // (xfr.configure <vmid> <vmconfig>)
Sxpr oxfr_err; // (xfr.err <code>)
Sxpr oxfr_hello; // (xfr.hello <major> <minor>)
-Sxpr oxfr_migrate; // (xfr.migrate <vmid> <vmconfig> <host> <port>)
+Sxpr oxfr_migrate; // (xfr.migrate <vmid> <vmconfig> <host> <port> <live> <resource>)
Sxpr oxfr_migrate_ok;// (xfr.migrate.ok <value>)
Sxpr oxfr_progress; // (xfr.progress <percent> <rate: kb/s>)
+Sxpr oxfr_restore; // (xfr.restore <file>)
+Sxpr oxfr_restore_ok;// (xfr.restore.ok <vmid>)
Sxpr oxfr_save; // (xfr.save <vmid> <vmconfig> <file>)
Sxpr oxfr_save_ok; // (xfr.save.ok)
+Sxpr oxfr_vm_destroy;// (xfr.vm.destroy <vmid>)
Sxpr oxfr_vm_suspend;// (xfr.vm.suspend <vmid>)
Sxpr oxfr_xfr; // (xfr.xfr <vmid>)
Sxpr oxfr_xfr_ok; // (xfr.xfr.ok <vmid>)
@@ -108,8 +115,11 @@ void xfr_init(void){
oxfr_migrate = intern("xfr.migrate");
oxfr_migrate_ok = intern("xfr.migrate.ok");
oxfr_progress = intern("xfr.progress");
+ oxfr_restore = intern("xfr.restore");
+ oxfr_restore_ok = intern("xfr.restore.ok");
oxfr_save = intern("xfr.save");
oxfr_save_ok = intern("xfr.save.ok");
+ oxfr_vm_destroy = intern("xfr.vm.destroy");
oxfr_vm_suspend = intern("xfr.vm.suspend");
oxfr_xfr = intern("xfr.xfr");
oxfr_xfr_ok = intern("xfr.xfr.ok");
@@ -161,6 +171,25 @@ void usage(int err){
exit(err ? 1 : 0);
}
+typedef struct Args {
+ int bufsize;
+ unsigned long port;
+ int verbose;
+ int compress;
+} Args;
+
+/** Transfer states. */
+enum {
+ XFR_INIT,
+ XFR_HELLO,
+ XFR_STATE,
+ XFR_RUN,
+ XFR_FAIL,
+ XFR_DONE,
+ XFR_MAX
+};
+
+#ifndef SXPR_PARSER_MAIN
/** Short options. Options followed by ':' take an argument. */
static char *short_opts = (char[]){
OPT_PORT, ':',
@@ -180,23 +209,12 @@ static struct option const long_opts[] = {
{ NULL, 0, NULL, 0 }
};
-typedef struct Args {
- int bufsize;
- unsigned long port;
- int verbose;
- int compress;
-} Args;
+/** Xfrd arguments. */
+static Args _args = {};
-/** Transfer states. */
-enum {
- XFR_INIT,
- XFR_HELLO,
- XFR_STATE,
- XFR_RUN,
- XFR_FAIL,
- XFR_DONE,
- XFR_MAX
-};
+/** Xfrd arguments. */
+static Args *args = &_args;
+#endif
/** Initialize an array element for a constant to its string name. */
#define VALDEF(val) { val, #val }
@@ -230,6 +248,8 @@ typedef struct XfrState {
unsigned long xfr_port;
char *xfr_host;
uint32_t vmid_new;
+ int live;
+ int resource;
} XfrState;
/** Get the name of a transfer state.
@@ -306,12 +326,6 @@ int XfrState_first_err_state(XfrState *s){
return s->err_state;
}
-/** Xfrd arguments. */
-static Args _args = {};
-
-/** Xfrd arguments. */
-static Args *args = &_args;
-
/** Set xfrd default arguments.
*
* @param args arguments to set
@@ -324,8 +338,7 @@ void set_defaults(Args *args){
int stringof(Sxpr exp, char **s){
int err = 0;
- dprintf(">\n");
- objprint(iostdout, exp, PRINT_TYPE); IOStream_print(iostdout, "\n");
+ //dprintf(">\n"); objprint(iostdout, exp, PRINT_TYPE); IOStream_print(iostdout, "\n");
if(ATOMP(exp)){
*s = atom_name(exp);
} else if(STRINGP(exp)){
@@ -334,7 +347,7 @@ int stringof(Sxpr exp, char **s){
err = -EINVAL;
*s = NULL;
}
- dprintf("< err=%d s=%s\n", err, *s);
+ //dprintf("< err=%d s=%s\n", err, *s);
return err;
}
@@ -342,8 +355,7 @@ int intof(Sxpr exp, int *v){
int err = 0;
char *s;
unsigned long l;
- dprintf(">\n");
- objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+ //dprintf(">\n"); objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
if(INTP(exp)){
*v = OBJ_INT(exp);
} else {
@@ -353,7 +365,7 @@ int intof(Sxpr exp, int *v){
*v = (int)l;
}
exit:
- dprintf("< err=%d v=%d\n", err, *v);
+ //dprintf("< err=%d v=%d\n", err, *v);
return err;
}
@@ -361,8 +373,7 @@ int addrof(Sxpr exp, uint32_t *v){
char *h;
unsigned long a;
int err = 0;
- dprintf(">\n");
- objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+ //dprintf(">\n"); objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
err = stringof(exp, &h);
if(err) goto exit;
if(get_host_address(h, &a)){
@@ -371,15 +382,14 @@ int addrof(Sxpr exp, uint32_t *v){
}
*v = a;
exit:
- dprintf("< err=%d v=%x\n", err, *v);
+ //dprintf("< err=%d v=%x\n", err, *v);
return err;
}
int portof(Sxpr exp, uint16_t *v){
char *s;
int err = 0;
- dprintf(">\n");
- objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+ //dprintf(">\n"); objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
if(INTP(exp)){
*v = get_ul(exp);
*v = htons(*v);
@@ -395,7 +405,7 @@ int portof(Sxpr exp, uint16_t *v){
*v = p;
}
exit:
- dprintf("< err=%d v=%u\n", err, *v);
+ //dprintf("< err=%d v=%u\n", err, *v);
return err;
}
@@ -468,7 +478,7 @@ int xfr_hello(Conn *conn){
err = Conn_sxpr(conn, &sxpr);
if(err) goto exit;
if(!sxpr_elementp(sxpr, oxfr_hello)){
- dprintf("> sxpr_elementp test failed\n");
+ wprintf("> sxpr_elementp test failed\n");
err = -EINVAL;
goto exit;
}
@@ -507,7 +517,6 @@ int xfr_send_hello(Conn *conn){
XFR_PROTO_MINOR);
if(err < 0) goto exit;
IOStream_flush(conn->out);
- dprintf("> xfr_response...\n");
err = xfr_response(conn);
exit:
dprintf("< err=%d\n", err);
@@ -538,6 +547,14 @@ int xfr_send_migrate_ok(Conn *conn, uint32_t vmid){
return (err < 0 ? err : 0);
}
+int xfr_send_restore_ok(Conn *conn, uint32_t vmid){
+ int err = 0;
+
+ err = IOStream_print(conn->out, "(%s %d)",
+ atom_name(oxfr_restore_ok), vmid);
+ return (err < 0 ? err : 0);
+}
+
int xfr_send_save_ok(Conn *conn){
int err = 0;
@@ -568,6 +585,28 @@ int xfr_vm_suspend(Conn *xend, uint32_t vmid){
return err;
}
+int xfr_send_destroy(Conn *conn, uint32_t vmid){
+ int err = 0;
+
+ err = IOStream_print(conn->out, "(%s %d)",
+ atom_name(oxfr_vm_destroy), vmid);
+ return (err < 0 ? err : 0);
+}
+
+/** Destroy a vm on behalf of save/migrate.
+ */
+int xfr_vm_destroy(Conn *xend, uint32_t vmid){
+ int err = 0;
+ dprintf("> vmid=%u\n", vmid);
+ err = xfr_send_destroy(xend, vmid);
+ if(err) goto exit;
+ IOStream_flush(xend->out);
+ err = xfr_response(xend);
+ exit:
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
/** Get vm state. Send transfer message.
*
* @param peer connection
@@ -583,8 +622,19 @@ int xfr_send_state(XfrState *state, Conn *xend, Conn *peer){
// Send xfr message and the domain state.
err = xfr_send_xfr(peer, state->vmid);
if(err) goto exit;
+ dprintf(">*** Sending domain %u\n", state->vmid);
err = xen_domain_snd(xend, peer->out,
- state->vmid, state->vmconfig, state->vmconfig_n);
+ state->vmid,
+ state->vmconfig, state->vmconfig_n,
+ state->live, state->resource);
+ dprintf(">*** Sent domain %u\n", state->vmid);
+ if(err) goto exit;
+ // Sending the domain suspends it, and there's no way back.
+ // So destroy it now. If anything goes wrong now it's too late.
+ dprintf(">*** Destroying domain %u\n", state->vmid);
+ err = xfr_vm_destroy(xend, state->vmid);
+ if(err) goto exit;
+ err = xfr_error(peer, err);
if(err) goto exit;
IOStream_flush(peer->out);
// Read the response from the peer.
@@ -671,7 +721,6 @@ int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t po
dprintf("> Xfr xfr_addr=%s:%d\n", inet_ntoa(xfr_addr), ntohs(xfr_port));
err = Conn_connect(peer, flags, xfr_addr, xfr_port);
if(err) goto exit;
- printf("\n");
XfrState_set_state(state, XFR_HELLO);
// Send hello message.
err = xfr_send_hello(peer);
@@ -685,10 +734,9 @@ int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t po
int plain_bytes = lzi_stream_plain_bytes(zio);
int comp_bytes = lzi_stream_comp_bytes(zio);
float ratio = lzi_stream_ratio(zio);
- dprintf("> Compression: plain %d bytes, compressed %d bytes, ratio %3.2f\n",
+ iprintf("> Compression: plain %d bytes, compressed %d bytes, ratio %3.2f\n",
plain_bytes, comp_bytes, ratio);
}
- printf("\n");
exit:
dprintf("> err=%d\n", err);
if(err && !XfrState_get_err(state)){
@@ -697,7 +745,7 @@ int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t po
Conn_close(peer);
if(!err){
t1 = time(NULL) - t0;
- dprintf("> Transfer complete in %lu seconds\n", t1);
+ iprintf("> Transfer complete in %lu seconds\n", t1);
}
dprintf("> done err=%d, notifying xend...\n", err);
xfr_send_done(state, xend);
@@ -709,16 +757,24 @@ int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t po
*/
int xfr_save(Args *args, XfrState *state, Conn *xend, char *file){
int err = 0;
+ int compress = 0;
IOStream *io = NULL;
dprintf("> file=%s\n", file);
- io = file_stream_fopen(file, "wb");
+ if(compress){
+ io = gzip_stream_fopen(file, "wb1");
+ } else {
+ io = file_stream_fopen(file, "wb");
+ }
if(!io){
- dprintf("> Failed to open %s\n", file);
- err = -EIO;
+ eprintf("> Failed to open %s\n", file);
+ err = -EINVAL;
goto exit;
}
- err = xen_domain_snd(xend, io, state->vmid, state->vmconfig, state->vmconfig_n);
+ err = xen_domain_snd(xend, io,
+ state->vmid,
+ state->vmconfig, state->vmconfig_n,
+ 0, 0);
if(err){
err = xfr_error(xend, err);
} else {
@@ -729,6 +785,49 @@ int xfr_save(Args *args, XfrState *state, Conn *xend, char *file){
IOStream_close(io);
IOStream_free(io);
}
+ if(err){
+ unlink(file);
+ }
+ dprintf("< err=%d\n", err);
+ return err;
+}
+
+/** Restore a vm from file.
+ *
+ * @return 0 on success, error code otherwise
+ */
+int xfr_restore(Args *args, XfrState *state, Conn *xend, char *file){
+ int err = 0;
+ IOStream *io = NULL;
+ int configured=0;
+
+ dprintf("> file=%s\n", file);
+ io = gzip_stream_fopen(file, "rb");
+ if(!io){
+ eprintf("> Failed to open %s\n", file);
+ err = -EINVAL;
+ goto exit;
+ }
+ err = xen_domain_rcv(io,
+ &state->vmid_new,
+ &state->vmconfig, &state->vmconfig_n,
+ &configured);
+ if(err) goto exit;
+ if(!configured){
+ err = xen_domain_configure(state->vmid_new, state->vmconfig, state->vmconfig_n);
+ if(err) goto exit;
+ }
+ err = xen_domain_unpause(state->vmid_new);
+ exit:
+ if(io){
+ IOStream_close(io);
+ IOStream_free(io);
+ }
+ if(err){
+ xfr_error(xend, err);
+ } else {
+ xfr_send_restore_ok(xend, state->vmid_new);
+ }
dprintf("< err=%d\n", err);
return err;
}
@@ -742,14 +841,34 @@ int xfr_save(Args *args, XfrState *state, Conn *xend, char *file){
int xfr_recv(Args *args, XfrState *state, Conn *peer){
int err = 0;
time_t t0 = time(NULL), t1;
-
- dprintf(">\n");
- err = xen_domain_rcv(peer->in, &state->vmid_new, &state->vmconfig, &state->vmconfig_n);
+ Sxpr sxpr;
+ int configured=0;
+
+ dprintf("> peer=%s\n", inet_ntoa(peer->addr.sin_addr));
+ // If receiving from localhost set configured so that that xen_domain_rcv()
+ // does not attempt to configure the new domain. This is because the old
+ // domain still exists and will make it fail.
+ if(peer->addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)){
+ dprintf("> Peer is localhost\n");
+ configured = 1;
+ }
+ err = xen_domain_rcv(peer->in,
+ &state->vmid_new,
+ &state->vmconfig, &state->vmconfig_n,
+ &configured);
if(err) goto exit;
-
- err = xen_domain_configure(state->vmid_new, state->vmconfig, state->vmconfig_n);
+ // Read from the peer. This is just so we wait before configuring.
+ // When migrating to the same host the peer must destroy the domain
+ // before we configure the new one.
+ err = Conn_sxpr(peer, &sxpr);
if(err) goto exit;
-
+ if(!configured){
+ dprintf("> Configuring...\n");
+ err = xen_domain_configure(state->vmid_new, state->vmconfig, state->vmconfig_n);
+ if(err) goto exit;
+ err = xen_domain_unpause(state->vmid_new);
+ if(err) goto exit;
+ }
// Report new domain id to peer.
err = xfr_send_xfr_ok(peer, state->vmid_new);
if(err) goto exit;
@@ -758,7 +877,7 @@ int xfr_recv(Args *args, XfrState *state, Conn *peer){
exit:
if(!err){
t1 = time(NULL) - t0;
- dprintf("> Transfer complete in %lu seconds\n", t1);
+ iprintf("> Transfer complete in %lu seconds\n", t1);
}
if(err){
xfr_error(peer, err);
@@ -783,14 +902,14 @@ int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){
dprintf(">\n");
err = Conn_init(conn, flags, peersock, peer_in);
if(err) goto exit;
- dprintf(">xfr_hello... \n");
+ //dprintf(">xfr_hello... \n");
err = xfr_hello(conn);
if(err) goto exit;
- dprintf("> sxpr...\n");
+ //dprintf("> sxpr...\n");
err = Conn_sxpr(conn, &sxpr);
if(err) goto exit;
- dprintf("> sxpr=\n");
- objprint(iostdout, sxpr, PRINT_TYPE); IOStream_print(iostdout, "\n");
+ //dprintf("> sxpr=\n");
+ //objprint(iostdout, sxpr, PRINT_TYPE); IOStream_print(iostdout, "\n");
if(sxpr_elementp(sxpr, oxfr_migrate)){
// Migrate message from xend.
uint32_t addr;
@@ -808,6 +927,10 @@ int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){
if(err) goto exit;
err = portof(sxpr_childN(sxpr, n++, ONONE), &port);
if(err) goto exit;
+ err = intof(sxpr_childN(sxpr, n++, ONONE), &state->live);
+ if(err) goto exit;
+ err = intof(sxpr_childN(sxpr, n++, ONONE), &state->resource);
+ if(err) goto exit;
err = xfr_send(args, state, conn, addr, port);
} else if(sxpr_elementp(sxpr, oxfr_save)){
@@ -826,6 +949,17 @@ int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){
if(err) goto exit;
err = xfr_save(args, state, conn, file);
+ } else if(sxpr_elementp(sxpr, oxfr_restore)){
+ // Restore message from xend.
+ char *file;
+ XfrState _state = {}, *state = &_state;
+ int n = 0;
+
+ dprintf("> xfr.restore\n");
+ err = stringof(sxpr_childN(sxpr, n++, ONONE), &file);
+ if(err) goto exit;
+ err = xfr_restore(args, state, conn, file);
+
} else if(sxpr_elementp(sxpr, oxfr_xfr)){
// Xfr message from peer xfrd.
XfrState _state = {}, *state = &_state;
@@ -839,7 +973,7 @@ int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){
} else{
// Anything else is invalid.
err = -EINVAL;
- dprintf("> Invalid message: ");
+ eprintf("> Invalid message: ");
objprint(iostderr, sxpr, 0);
IOStream_print(iostderr, "\n");
xfr_error(conn, err);
@@ -863,7 +997,7 @@ int xfrd_accept(Args *args, int sock){
pid_t pid;
int err = 0;
- dprintf(">\n");
+ dprintf("> sock=%d\n", sock);
dprintf("> accept...\n");
peersock = accept(sock, peer, &peer_n);
dprintf("> accept=%d\n", peersock);
@@ -872,8 +1006,8 @@ int xfrd_accept(Args *args, int sock){
err = -errno;
goto exit;
}
- iprintf("> Accepted connection from %s:%d\n",
- inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port));
+ iprintf("> Accepted connection from %s:%d on %d\n",
+ inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port), sock);
pid = fork();
if(pid > 0){
// Parent, fork succeeded.
@@ -1080,6 +1214,7 @@ int xfrd_main(Args *args){
return err;
}
+#ifndef SXPR_PARSER_MAIN
/** Parse command-line arguments and call the xfrd main program.
*
* @param arg argument count
@@ -1090,7 +1225,14 @@ int main(int argc, char *argv[]){
int err = 0;
int key = 0;
int long_index = 0;
+ static const char * LOGFILE = "/var/log/xfrd.log";
+#ifndef DEBUG
+ freopen(LOGFILE, "w+", stdout);
+ fclose(stderr);
+ stderr = stdout;
+#endif
+ dprintf(">\n");
set_defaults(args);
while(1){
key = getopt_long(argc, argv, short_opts, long_opts, &long_index);
@@ -1126,4 +1268,4 @@ int main(int argc, char *argv[]){
}
return (err ? 1 : 0);
}
-
+#endif
diff --git a/tools/xfrd/xfrd.h b/tools/xfrd/xfrd.h
index 9de464549b..0671b383eb 100644
--- a/tools/xfrd/xfrd.h
+++ b/tools/xfrd/xfrd.h
@@ -13,4 +13,5 @@
struct Conn;
extern int xfr_vm_suspend(struct Conn *xend, uint32_t vmid);
+extern int xfr_vm_destroy(struct Conn *xend, uint32_t vmid);
#endif